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.