GophHub - go4retro/tcpser/src/bridge.c


Raw File

    1	#include <stdio.h>
    2	
    3	#include <sys/socket.h>   // for recv...
    4	#include <unistd.h>       // for read...
    5	#include <stdlib.h>       // for exit...
    6	#include <sys/param.h>
    7	#include <sys/time.h>
    8	#include <pthread.h>
    9	
   10	#include "util.h"
   11	#include "debug.h"
   12	#include "nvt.h"
   13	#include "modem_core.h"
   14	#include "ip.h"
   15	#include "getcmd.h"
   16	
   17	#include "bridge.h"
   18	
   19	const char MDM_NO_ANSWER[] = "NO ANSWER\n";
   20	
   21	int accept_connection(modem_config *cfg) {
   22	  LOG_ENTER();
   23	
   24	  if(-1 != line_accept(&cfg->line_data)) {
   25	    if(cfg->direct_conn == TRUE) {
   26	      cfg->conn_type = MDM_CONN_INCOMING;
   27	      mdm_off_hook(cfg);
   28	    } else {
   29	      //line_write(cfg,(unsigned char*)CONNECT_NOTICE,strlen(CONNECT_NOTICE));
   30	      cfg->ring_ctr = 0;
   31	      cfg->s[S_REG_RING_COUNT] = 0;
   32	      mdm_send_ring(cfg);  // send the first RING
   33	    }
   34	    // tell parent I got it.
   35	    LOG(LOG_DEBUG, "Informing parent task that I am busy");
   36	    writePipe(cfg->mp[0][1], MSG_BUSY);
   37	  }
   38	  LOG_EXIT();
   39	  return 0;
   40	}
   41	
   42	int parse_ip_data(modem_config *cfg, unsigned char *data, int len) {
   43	  // I'm going to cheat and assume it comes in chunks.
   44	  int i = 0;
   45	  unsigned char ch;
   46	  unsigned char text[1025];
   47	  int text_len = 0;
   48	
   49	  if(cfg->line_data.is_data_received == FALSE) {
   50	    cfg->line_data.is_data_received = TRUE;
   51	    if((data[0] == 0xff) || (data[0] == 0x1a)) {
   52	      //line_write(cfg, (char*)TELNET_NOTICE,strlen(TELNET_NOTICE));
   53	      LOG(LOG_INFO, "Detected telnet");
   54	      // TODO add in telnet stuff
   55	      cfg->line_data.is_telnet = TRUE;
   56	      /* we need to let the other end know that our end will
   57	       * handle the echo - otherwise "true" telnet clients like
   58	       * those that come with Linux & Windows will echo characters
   59	       * typed and you'll end up with doubled characters if the remote
   60	       * host is echoing as well...
   61	       * - gwb
   62	       */
   63	      send_nvt_command(cfg->line_data.fd, &cfg->line_data.nvt_data, NVT_WILL, NVT_OPT_ECHO);
   64	    }
   65	  }
   66	
   67	  if(cfg->line_data.is_telnet == TRUE) {
   68	    // once the serial port has seen a bit of data and telnet is active,
   69	    // we can decide on binary transmit, not before
   70	    if(cfg->is_binary_negotiated == FALSE) {
   71	      if(dce_get_parity(&cfg->dce_data)) {
   72	        // send explicit notice this connection is not 8 bit clean
   73	        send_nvt_command(cfg->line_data.fd,
   74	                         &cfg->line_data.nvt_data,
   75	                         NVT_WONT,
   76	                         NVT_OPT_TRANSMIT_BINARY
   77	                        );
   78	        send_nvt_command(cfg->line_data.fd,
   79	                         &cfg->line_data.nvt_data,
   80	                         NVT_DONT,
   81	                         NVT_OPT_TRANSMIT_BINARY
   82	                        );
   83	      } else {
   84	        send_nvt_command(cfg->line_data.fd,
   85	                         &cfg->line_data.nvt_data,
   86	                         NVT_WILL,
   87	                         NVT_OPT_TRANSMIT_BINARY
   88	                        );
   89	        send_nvt_command(cfg->line_data.fd,
   90	                         &cfg->line_data.nvt_data,
   91	                         NVT_DO,
   92	                         NVT_OPT_TRANSMIT_BINARY
   93	                        );
   94	      }
   95	      cfg->is_binary_negotiated = TRUE;
   96	    }
   97	    while(i < len) {
   98	      ch = data[i];
   99	      if(NVT_IAC == ch) {
  100	        // what if we roll off the end?
  101	        ch = data[i + 1];
  102	        switch(ch) {
  103	          case NVT_WILL:
  104	          case NVT_DO:
  105	          case NVT_WONT:
  106	          case NVT_DONT:
  107	            /// again, overflow issues...
  108	            LOG(LOG_INFO, "Parsing nvt command");
  109	            parse_nvt_command(&cfg->dce_data,
  110	                              cfg->line_data.fd,
  111	                              &cfg->line_data.nvt_data,
  112	                              ch,
  113	                              data[i + 2]
  114	                             );
  115	            i += 3;
  116	            break;
  117	          case NVT_SB:      // sub negotiation
  118	            // again, overflow...
  119	            i += parse_nvt_subcommand(&cfg->dce_data, 
  120	                                      cfg->line_data.fd, 
  121	                                      &cfg->line_data.nvt_data, 
  122	                                      data + i, 
  123	                                      len - i
  124	                                     );
  125	            break;
  126	          case NVT_IAC:
  127	            if (cfg->line_data.nvt_data.binary_recv)
  128	              text[text_len++] = NVT_IAC;
  129	              // fall through to skip this sequence
  130	          default:
  131	            // ignore...
  132	            i += 2;
  133	        }
  134	      } else {
  135	        text[text_len++] = data[i++];
  136	      }
  137	      if(text_len == 1024) {
  138	        text[text_len] = 0;
  139	        // write to serial...
  140	        mdm_write(cfg, text, text_len);
  141	        text_len = 0;
  142	      }
  143	    }
  144	    if(text_len) {
  145	      text[text_len] = 0;
  146	      // write to serial...
  147	      mdm_write(cfg, text, text_len);
  148	    }
  149	  } else {
  150	    mdm_write(cfg, data, len);
  151	  }
  152	  return 0;
  153	}
  154	
  155	void *ip_thread(void *arg) {
  156	  modem_config* cfg = (modem_config *)arg;
  157	
  158	  int action_pending = FALSE;
  159	  fd_set readfs;
  160	  int max_fd;
  161	  int res = 0;
  162	  unsigned char buf[256];
  163	  int rc;
  164	
  165	
  166	  LOG_ENTER();
  167	  while(TRUE) {
  168	    FD_ZERO(&readfs);
  169	    FD_SET(cfg->cp[1][0], &readfs);
  170	    max_fd = cfg->cp[1][0];
  171	    if(action_pending == FALSE
  172	       && cfg->conn_type != MDM_CONN_NONE
  173	       && cfg->is_cmd_mode == FALSE
  174	       && cfg->line_data.fd > -1
  175	       && cfg->line_data.is_connected == TRUE
  176	      ) {
  177	      FD_SET(cfg->line_data.fd, &readfs); 
  178	      max_fd=MAX(max_fd, cfg->line_data.fd);
  179	    }
  180	    max_fd++;
  181	    rc = select(max_fd, &readfs, NULL, NULL, NULL);
  182	    if(rc == -1) {
  183	      ELOG(LOG_WARN, "Select returned error");
  184	      // handle error
  185	    } else {
  186	      // we got data
  187	      if (cfg->line_data.is_connected == TRUE && FD_ISSET(cfg->line_data.fd, &readfs)) {  // socket
  188	        LOG(LOG_DEBUG, "Data available on socket");
  189	        res = line_read(&cfg->line_data, buf, sizeof(buf));
  190	        //res = recv(cfg->line_data.fd, buf, sizeof(buf), 0);
  191	        if(0 >= res) {
  192	          LOG(LOG_INFO, "No socket data read, assume closed peer");
  193	          writePipe(cfg->cp[0][1], MSG_DISCONNECT);
  194	          action_pending = TRUE;
  195	        } else {
  196	          LOG(LOG_DEBUG, "Read %d bytes from socket", res);
  197	          buf[res] = 0;
  198	          parse_ip_data(cfg, buf, res);
  199	        }
  200	      }
  201	      if (FD_ISSET(cfg->cp[1][0], &readfs)) {  // pipe
  202	
  203	        res = readPipe(cfg->cp[1][0], buf, sizeof(buf) - 1);
  204	        LOG(LOG_DEBUG, "IP thread notified");
  205	        action_pending = FALSE;
  206	      }
  207	    }
  208	  }
  209	  LOG_EXIT();
  210	}
  211	
  212	void *ctrl_thread(void *arg) {
  213	  modem_config* cfg = (modem_config *)arg;
  214	  int status;
  215	  int new_status;
  216	
  217	  LOG_ENTER();
  218	  status = dce_get_control_lines(&cfg->dce_data);
  219	  while(status > -1) {
  220	    new_status = dce_check_control_lines(&cfg->dce_data);
  221	    if(new_status > -1 && status != new_status) {
  222	      LOG(LOG_DEBUG, "Control Line Change");
  223	      if((new_status & DCE_CL_DTR) != (status & DCE_CL_DTR)) {
  224	        if((new_status & DCE_CL_DTR)) {
  225	          LOG(LOG_INFO, "DTR has gone high");
  226	          writePipe(cfg->wp[0][1], MSG_DTR_UP);
  227	        } else {
  228	          LOG(LOG_INFO, "DTR has gone low");
  229	          writePipe(cfg->wp[0][1], MSG_DTR_DOWN);
  230	        }
  231	      }
  232	      if((new_status & DCE_CL_LE) != (status & DCE_CL_LE)) {
  233	        if((new_status & DCE_CL_LE)) {
  234	          LOG(LOG_INFO, "Link has come up");
  235	          writePipe(cfg->wp[0][1], MSG_LE_UP);
  236	        } else {
  237	          LOG(LOG_INFO, "Link has gone down");
  238	          writePipe(cfg->wp[0][1], MSG_LE_DOWN);
  239	        }
  240	      }
  241	    }
  242	    status = new_status;
  243	  }
  244	  LOG_EXIT();
  245	  // need to quit application, as status cannot be obtained.
  246	  exit(-1);
  247	}
  248	
  249	void *bridge_task(void *arg) {
  250	  modem_config *cfg = (modem_config *)arg;
  251	  struct timeval timer;  
  252	  struct timeval *ptimer;  
  253	  int max_fd = 0;
  254	  fd_set readfs;
  255	  int res = 0;
  256	  unsigned char buf[256];
  257	  int rc = 0;
  258	
  259	  int last_conn_type;
  260	  int last_cmd_mode = cfg->is_cmd_mode;
  261	
  262	
  263	  LOG_ENTER();
  264	
  265	  if(-1 == pipe(cfg->wp[0])) {
  266	    ELOG(LOG_FATAL, "Control line watch task incoming IPC pipe could not be created");
  267	    exit(-1);
  268	  }
  269	  if(-1 == pipe(cfg->cp[0])) {
  270	    ELOG(LOG_FATAL, "IP thread incoming IPC pipe could not be created");
  271	    exit(-1);
  272	  }
  273	  if(-1 == pipe(cfg->cp[1])) {
  274	    ELOG(LOG_FATAL, "IP thread outgoing IPC pipe could not be created");
  275	    exit(-1);
  276	  }
  277	  if(dce_connect(&cfg->dce_data) < 0) {
  278	    ELOG(LOG_FATAL, "Could not open serial port %s", cfg->dce_data.tty);
  279	    exit(-1);
  280	  }
  281	
  282	  spawn_thread((void *)ctrl_thread, (void *)cfg, "CTRL");
  283	  spawn_thread((void *)ip_thread, (void *)cfg, "IP");
  284	
  285	  mdm_set_control_lines(cfg);
  286	  last_conn_type = cfg->conn_type;
  287	  cfg->allow_transmit = FALSE;
  288	  // call some functions behind the scenes
  289	  if(cfg->cur_line_idx) {
  290	    mdm_parse_cmd(cfg);
  291	  }
  292	  if (cfg->direct_conn == TRUE) {
  293	    if(strlen((char *)cfg->direct_conn_num) > 0 &&
  294	       cfg->direct_conn_num[0] != ':') {
  295	        // we have a direct number to connect to.
  296	      strncpy(cfg->dialno, cfg->direct_conn_num, sizeof(cfg->dialno));
  297	      if(0 != line_connect(&cfg->line_data, cfg->dialno)) {
  298	        LOG(LOG_FATAL, "Cannot connect to Direct line address!");
  299	        // probably should exit...
  300	        exit(-1);
  301	      } else {
  302	        cfg->conn_type = MDM_CONN_OUTGOING;
  303	      }
  304	    }
  305	  }
  306	  cfg->allow_transmit = TRUE;
  307	  for(;;) {
  308	    if(last_conn_type != cfg->conn_type) {
  309	      LOG(LOG_ALL, "Connection status change, handling");
  310	      writePipe(cfg->cp[1][1], MSG_NOTIFY);
  311	      if(cfg->conn_type == MDM_CONN_OUTGOING) {
  312	        if(strlen(cfg->local_connect) > 0) {
  313	          writeFile(cfg->local_connect, cfg->line_data.fd);
  314	        }
  315	        if(strlen(cfg->remote_connect) > 0) {
  316	          writeFile(cfg->remote_connect, cfg->line_data.fd);
  317	        }
  318	      } else if(cfg->conn_type == MDM_CONN_INCOMING) {
  319	        if(strlen(cfg->local_answer) > 0) {
  320	          writeFile(cfg->local_answer, cfg->line_data.fd);
  321	        }
  322	        if(strlen(cfg->remote_answer) > 0) {
  323	          writeFile(cfg->remote_answer, cfg->line_data.fd);
  324	        }
  325	      }
  326	      last_conn_type = cfg->conn_type;
  327	    }
  328	    if(last_cmd_mode != cfg->is_cmd_mode) {
  329	      writePipe(cfg->cp[1][1], MSG_NOTIFY);
  330	      last_cmd_mode = cfg->is_cmd_mode;
  331	    }
  332	    LOG(LOG_ALL, "Waiting for modem/control line/timer/socket activity");
  333	    LOG(LOG_ALL, "CMD:%d, DCE:%d, LINE:%d, TYPE:%d, HOOK:%d", cfg->is_cmd_mode, cfg->dce_data.is_connected, cfg->line_data.is_connected, cfg->conn_type, cfg->is_off_hook);
  334	    FD_ZERO(&readfs);
  335	    max_fd = cfg->mp[1][0];
  336	    FD_SET(cfg->mp[1][0], &readfs);
  337	    if(cfg->dce_data.is_connected) {
  338	      max_fd = MAX(max_fd, cfg->dce_data.ifd);
  339	      FD_SET(cfg->dce_data.ifd, &readfs);
  340	    }
  341	    max_fd = MAX(max_fd, cfg->wp[0][0]);
  342	    FD_SET(cfg->wp[0][0], &readfs);
  343	    max_fd = MAX(max_fd, cfg->cp[0][0]);
  344	    FD_SET(cfg->cp[0][0], &readfs);
  345	    ptimer = NULL;
  346	    if(cfg->is_cmd_mode == FALSE) {
  347	      if(cfg->pre_break_delay == FALSE || cfg->break_len == 3) {
  348	        LOG(LOG_ALL, "Setting timer for break delay");
  349	        long long usec;
  350	        usec = cfg->s[S_REG_GUARD_TIME] * 20000;
  351	        timer.tv_sec = usec / 1000000;
  352	        timer.tv_usec = usec % 1000000;
  353	        ptimer = &timer;
  354	      } else if(cfg->pre_break_delay == TRUE && cfg->break_len > 0) {
  355	        LOG(LOG_ALL, "Setting timer for inter-break character delay");
  356	        timer.tv_sec = 1;   // 1 second
  357	        timer.tv_usec = 0;
  358	        ptimer = &timer;
  359	      } else if (cfg->s[S_REG_INACTIVITY_TIME] != 0) {
  360	        LOG(LOG_ALL, "Setting timer for inactivity delay");
  361	        timer.tv_sec = cfg->s[S_REG_INACTIVITY_TIME] * 10;
  362	        timer.tv_usec = 0;
  363	        ptimer = &timer;
  364	      }
  365	    } else if(cfg->is_ringing == TRUE // TODO Not sure how we can be ringing with a connection or a lack of IP connection, but leaving in for now.
  366	              && cfg->conn_type == MDM_CONN_NONE
  367	              && cfg->line_data.is_connected == TRUE
  368	             ) {
  369	        LOG(LOG_ALL, "Setting timer for rings");
  370	        if(cfg->ring_ctr)
  371	          timer.tv_sec = 3;   // 3 seconds until next RING send
  372	        else
  373	          timer.tv_sec = 1;   // 1 second for RI to be high
  374	        timer.tv_usec = 0;
  375	        ptimer = &timer;
  376	    }
  377	    max_fd++;
  378	    rc = select(max_fd, &readfs, NULL, NULL, ptimer);
  379	    if(rc == -1) {
  380	      ELOG(LOG_WARN, "Select returned error");
  381	      // handle error
  382	    } else if(rc == 0) {
  383	      // timer popped.
  384	      // TODO ring counter should go back to 0 after 8 secs of no ringing.
  385	      cfg->ring_ctr = 1 - cfg->ring_ctr;
  386	      if(cfg->is_cmd_mode == TRUE
  387	         && cfg->conn_type == MDM_CONN_NONE
  388	         && cfg->line_data.is_connected == TRUE
  389	        ) {
  390	        if(0 == cfg->ring_ctr) {
  391	          if(cfg->s[0] == 0 && cfg->s[S_REG_RING_COUNT] == 10) {
  392	            // not going to answer, send some data back to IP and disconnect.
  393	            if(strlen(cfg->no_answer) == 0) {
  394	              line_write(&cfg->line_data, (unsigned char *)MDM_NO_ANSWER, strlen(MDM_NO_ANSWER));
  395	            } else {
  396	              writeFile(cfg->no_answer, cfg->line_data.fd);
  397	            }
  398	            cfg->is_ringing = FALSE;
  399	            if(cfg->direct_conn) {
  400	              LOG(LOG_INFO, "Direct connection active, maintaining link");
  401	            } else {
  402	              line_disconnect(&cfg->line_data);
  403	            }
  404	            //mdm_disconnect(cfg, FALSE); // not sure need to do a disconnect here, no connection
  405	          } else {
  406	            mdm_send_ring(cfg);
  407	          }
  408	        }
  409	      } else {
  410	        mdm_handle_timeout(cfg);
  411	      }
  412	      mdm_set_control_lines(cfg);
  413	    }
  414	    if (FD_ISSET(cfg->dce_data.ifd, &readfs)) {  // serial port
  415	      LOG(LOG_DEBUG, "Data available on serial port");
  416	      res = mdm_read(cfg, buf, sizeof(buf));
  417	      if(res > 0) {
  418	        if(cfg->conn_type == MDM_CONN_NONE
  419	           && !cfg->is_cmd_mode
  420	           && cfg->is_off_hook) {
  421	          // this handles the case where atdt/ata goes off hook, but no
  422	          // connection
  423	          mdm_disconnect(cfg, FALSE);
  424	        } else {
  425	          mdm_parse_data(cfg, buf, res);
  426	        }
  427	      }
  428	    }
  429	    if (FD_ISSET(cfg->wp[0][0], &readfs)) {  // control pipe
  430	      res = readPipe(cfg->wp[0][0], buf, sizeof(buf) - 1);
  431	      LOG(LOG_DEBUG, "Received %s from control line watch task", buf);
  432	      for(int i = 0; i < res ; i++) {
  433	        switch (buf[0]) {
  434	          case MSG_DTR_DOWN:
  435	            // DTR drop, close any active connection and put
  436	            // in cmd_mode
  437	            mdm_disconnect(cfg, FALSE);
  438	            break;
  439	          default:
  440	            break;
  441	        }
  442	      }
  443	    }
  444	    if (FD_ISSET(cfg->cp[0][0], &readfs)) {  // ip thread pipe
  445	      res = readPipe(cfg->cp[0][0], buf, sizeof(buf));
  446	      LOG(LOG_DEBUG, "Received %c from ip thread", buf[0]);
  447	      switch (buf[0]) {
  448	        case MSG_DISCONNECT:
  449	          if(cfg->direct_conn == TRUE) {
  450	            // what should we do here...
  451	            LOG(LOG_ERROR, "Direct Connection Link broken, disconnecting and awaiting new direct connection");
  452	            mdm_disconnect(cfg, TRUE);
  453	          } else {
  454	            mdm_disconnect(cfg, FALSE);
  455	          }
  456	          break;
  457	      }
  458	    }
  459	    if (FD_ISSET(cfg->mp[1][0], &readfs)) {  // parent pipe
  460	      LOG(LOG_DEBUG, "Data available on incoming IPC pipe");
  461	      res = readPipe(cfg->mp[1][0], buf, sizeof(buf));
  462	      switch (buf[0]) {
  463	        case MSG_CALLING:       // accept connection.
  464	          accept_connection(cfg);
  465	          break;
  466	      }
  467	    }
  468	  }
  469	  LOG_EXIT();
  470	}
  471	

Generated by GNU Enscript 1.6.6, and GophHub 1.3.