1 #include 2 3 #include // for recv... 4 #include // for read... 5 #include // for exit... 6 #include 7 #include 8 #include 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 }