├── CHANGES ├── Makefile ├── Makefile.solaris ├── Makefile.win32 ├── README.md ├── bridge └── src ├── bridge.c ├── bridge.h ├── dce.c ├── dce.h ├── debug.c ├── debug.h ├── getcmd.c ├── getcmd.h ├── init.c ├── init.h ├── ip.c ├── ip.h ├── ip232.c ├── ip232.h ├── line.c ├── line.h ├── modem_core.c ├── modem_core.h ├── nvt.c ├── nvt.h ├── phone_book.c ├── phone_book.h ├── serial.c ├── serial.h ├── shared.c ├── shared.h ├── tcpser.c ├── util.c └── util.h /CHANGES: -------------------------------------------------------------------------------- 1 | 1.0 2 | Initial release 3 | 1.0rc2 4 | Cleaned up code 5 | 10.rc3 6 | changed char fields to unsigned char 7 | Changed many functions to return status 8 | Added macros for __FUNCTION__ keyword, for compiler sanity 9 | Added include headers needed for other UNIX platforms 10 | 1.0rc4 11 | Changed cfg->connection to cfg->conn_type 12 | Added support for send file text to connections on actions 13 | If data received while off-hook and no conn, go on-hook. 14 | Clean up parseRegister action in getcmd.c 15 | Add support for ':' and '-' extended commands 16 | Cleaned up functions to only exit at one point 17 | 1.0rc5 18 | Moved includes to be in correct order 19 | Fixed Q0 and Q1 (they were reversed) 20 | 1.0rc6 21 | Add support for direct connection 22 | 1.0rc7 23 | Fixed mislocated tcsetattr in serial.com:ser_init_conn 24 | 1.0rc8 25 | Fixed bug in ip_thread (calling a null number would turn on ip_thread, but no connection, generating a dump 26 | Fixed bugs in dce_set_flow_control 27 | Fixed behavior of parms parsing for &Z (complex) and S (simple) 28 | Defaulted code to not assume /dev/ttyS0 as serport 29 | 1.0rc9 30 | Fixed bug that would stop first session if second call came in 31 | Fixed outbound call not bringing DCD high. 32 | 1.0rc10 33 | Added some more debugging information 34 | Changed logging to go to stdout from stderr 35 | Fixed Phone book matching function to use logger 36 | 1.0rc11 37 | Added ip232 support with custom protocol for use with matching modified version of WinVICE 1.19 38 | Added full support for Telnet Binary Transmission (RFC 856) 39 | 1.0rc12 40 | Added code to hook SIG_IO to SIG_IGN. Newer versions of Linux are terminating when IO arrives. 41 | 42 | 1.0fz1 - Chris Osborn 2016-May-24 43 | Cleaned up all the warnings about unsigned char strings 44 | Strip parity when looking for commands, generate matching parity on modem responses 45 | Fixed bug in sending telnet sub-options. Now tcpser can connect to real telnet servers. 46 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | SRC=src 2 | SRCS = $(SRC)/bridge.c $(SRC)/debug.c $(SRC)/getcmd.c $(SRC)/ip.c $(SRC)/init.c $(SRC)/modem_core.c $(SRC)/nvt.c $(SRC)/serial.c $(SRC)/ip232.c $(SRC)/util.c $(SRC)/phone_book.c $(SRC)/shared.c $(SRC)/tcpser.c $(SRC)/line.c $(SRC)/dce.c 3 | OBJS = $(SRC)/bridge.o $(SRC)/debug.o $(SRC)/getcmd.o $(SRC)/ip.o $(SRC)/init.o $(SRC)/modem_core.o $(SRC)/nvt.o $(SRC)/serial.o $(SRC)/ip232.o $(SRC)/util.o $(SRC)/phone_book.o $(SRC)/shared.o $(SRC)/tcpser.o $(SRC)/dce.o $(SRC)/line.o 4 | CC = gcc 5 | DEF = 6 | CFLAGS = -g $(DEF) -Wall 7 | LDFLAGS = -lpthread 8 | DEPEND = makedepend $(DEF) $(CFLAGS) 9 | 10 | all: tcpser 11 | 12 | #.o.c: 13 | # $(CC) $(CFLAGS) -c $*.c 14 | 15 | $(SRCS): 16 | $(CC) $(CFLAGS) -c $*.c 17 | 18 | tcpser: $(OBJS) 19 | $(CC) -g -o $@ $(OBJS) $(LDFLAGS) 20 | 21 | depend: $(SRCS) 22 | $(DEPEND) $(SRCS) 23 | 24 | clean: 25 | -rm tcpser *.bak $(SRC)/*~ $(SRC)/*.o $(SRC)/*.bak core 26 | 27 | 28 | # DO NOT DELETE THIS LINE -- make depend depends on it. 29 | 30 | -------------------------------------------------------------------------------- /Makefile.solaris: -------------------------------------------------------------------------------- 1 | SRC=src 2 | SRCS = $(SRC)/bridge.c $(SRC)/debug.c $(SRC)/getcmd.c $(SRC)/ip.c $(SRC)/init.c $(SRC)/modem_core.c $(SRC)/nvt.c $(SRC)/serial.c $(SRC)/ip232.c $(SRC)/util.c $(SRC)/phone_book.c $(SRC)/shared.c $(SRC)/tcpser.c $(SRC)/line.c $(SRC)/dce.c 3 | OBJS = $(SRC)/bridge.o $(SRC)/debug.o $(SRC)/getcmd.o $(SRC)/ip.o $(SRC)/init.o $(SRC)/modem_core.o $(SRC)/nvt.o $(SRC)/serial.o $(SRC)/ip232.o $(SRC)/util.o $(SRC)/phone_book.o $(SRC)/shared.o $(SRC)/tcpser.o $(SRC)/dce.o $(SRC)/line.o 4 | CC = gcc 5 | DEF = 6 | CFLAGS = -O $(DEF) -Wall 7 | LDFLAGS = -lpthread -lsocket -lnsl -lresolv 8 | DEPEND = makedepend $(DEF) $(CFLAGS) 9 | 10 | all: tcpser 11 | 12 | #.o.c: 13 | # $(CC) $(CFLAGS) -c $*.c 14 | 15 | $(SRCS): 16 | $(CC) $(CFLAGS) -c $*.c 17 | 18 | tcpser: $(OBJS) 19 | $(CC) $(LDFLAGS) -o $@ $(OBJS) 20 | 21 | depend: $(SRCS) 22 | $(DEPEND) $(SRCS) 23 | 24 | clean: 25 | -rm tcpser *.bak $(SRC)/*~ $(SRC)/*.o $(SRC)/*.bak core 26 | 27 | 28 | # DO NOT DELETE THIS LINE -- make depend depends on it. 29 | -------------------------------------------------------------------------------- /Makefile.win32: -------------------------------------------------------------------------------- 1 | SRC=src 2 | SRCS = $(SRC)/bridge.c $(SRC)/debug.c $(SRC)/getcmd.c $(SRC)/ip.c $(SRC)/init.c $(SRC)/modem_core.c $(SRC)/nvt.c $(SRC)/serial.c $(SRC)/ip232.c $(SRC)/util.c $(SRC)/phone_book.c $(SRC)/shared.c $(SRC)/tcpser.c $(SRC)/line.c $(SRC)/dce.c 3 | OBJS = $(SRC)/bridge.o $(SRC)/debug.o $(SRC)/getcmd.o $(SRC)/ip.o $(SRC)/init.o $(SRC)/modem_core.o $(SRC)/nvt.o $(SRC)/serial.o $(SRC)/ip232.o $(SRC)/util.o $(SRC)/phone_book.o $(SRC)/shared.o $(SRC)/tcpser.o $(SRC)/dce.o $(SRC)/line.o 4 | CC = gcc 5 | DEF = 6 | CFLAGS = -O $(DEF) -Wall -DWIN32 7 | LDFLAGS = 8 | DEPEND = makedepend $(DEF) $(CFLAGS) 9 | 10 | all: tcpser 11 | 12 | #.o.c: 13 | # $(CC) $(CFLAGS) -c $*.c 14 | 15 | $(SRCS): 16 | $(CC) $(CFLAGS) -c $*.c 17 | 18 | tcpser: $(OBJS) 19 | $(CC) $(LDFLAGS) -o $@ $(OBJS) 20 | 21 | depend: $(SRCS) 22 | $(DEPEND) $(SRCS) 23 | 24 | clean: 25 | -rm tcpser.exe *.bak $(SRC)/*~ $(SRC)/*.o $(SRC)/*.bak core 26 | 27 | 28 | # DO NOT DELETE THIS LINE -- make depend depends on it. 29 | 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This is yet another fork of Jim Brain's tcpser serial to IP modem emulation program. 2 | 3 | The original source code can be found here: 4 | http://www.jbrain.com/pub/linux/serial/ 5 | 6 | My changes are based upon the rc12 archive dated 11Mar09. 7 | 8 | I fixed the bug with being unable to connect to real telnet servers. 9 | 10 | I also made the modem routines automatically detect parity. If even, 11 | odd, or mark parity is detected then the telnet connection will be 12 | limited to 7 bit. Space parity will be treated as 8N1 and will allow 13 | telnet binary mode. 14 | 15 | I also incorporated geneb's changes found at https://github.com/geneb/tcpser 16 | 17 | Chris Osborn 18 | http://insentricity.com 19 | 20 | --- 21 | 22 | ## Application 23 | 24 | TCPSER turns a PC serial port into an emulated Hayes compatible modem that 25 | uses TCP/IP for incoming and outgoing connections. It can be used to allow 26 | older applications and systems designed for modem use to operate on the 27 | Internet. TCPSER supports all standard Hayes commands, and understands 28 | extended and vendor proprietary commands (though it does not implement 29 | many of them). TCPSER can be used for both inbound and outbound connections. 30 | 31 | ## License 32 | 33 | TCPSER is distributed under the GPL 2.0 or later 34 | 35 | ## Executable/Building 36 | 37 | ### UNIX/Linux/BSD/macOS 38 | 39 | Simply clone the repository, and use the appropriate make command 40 | generate the exectutable. If unsure, try the default make command first. 41 | 42 | | OS | Command | 43 | |---------------------|----------------------------| 44 | | Default/Linux/macOS | `make` | 45 | | Solaris | `make -f Makefile.solaris` | 46 | | *BSD | `gmake` | 47 | 48 | ### Windows 95/OSR2/98/SE/ME/NT/2000/XP/2003 49 | 50 | Win32: make -f Makefile.win32 51 | 52 | Note that at least the cygwin1.dll library is required to operate tcpser under 53 | Windows. I recommend downloading a recent version from www.cygwin.com 54 | 55 | This version of tcpser supports setting up an ip232 port instead of using a 56 | real serial port. This is for use with the version of WinVICE 1.19 that has 57 | the ACIA fix and ip232 support, allowing WinVICE to use tcpser as if it was 58 | connected via a serial cable. 59 | 60 | ## Operation 61 | ``` 62 | tcpser -d -s -l -t ... 63 | ``` 64 | -or- 65 | ``` 66 | tcpser -v -s -l -t ... 67 | ``` 68 | ## Examples 69 | ``` 70 | tcpser -d /dev/ttyS0 -s 38400 -l 7 -tsSiI -i "s0=1" -p 6400 71 | ``` 72 | Will start tcpser on ttyS0 at 38400 bps, level 7 logging, tracing of 73 | inbound serial, outbound serial, inbound IP, outbound IP, init 74 | modem to answer after 1 ring, and listen for incoming connections on port 75 | 6400 76 | ``` 77 | tcpser -v 25232 -s 38400 -l 4 -p 23 78 | ``` 79 | Will set up an ip232 port at 25232, report 38400 bps connections, 80 | level 4 logging, and listen for incoming connections on port 23. 81 | 82 | tcpser -h will provide additional information 83 | 84 | tcpser can be configured to send the contents of a file upon: 85 | 86 | | Event | Flags | 87 | |--------------------|-------| 88 | | connect | -c -C | 89 | | answer | -a -A | 90 | | no-answer | -I | 91 | | busy | -B | 92 | | inactivity-timeout | -T | 93 | 94 | For connect and answer, there are separate options for sending a file to the 95 | local serial connection (-c, -a) and the remote IP connection (-C, -A). 96 | 97 | If tcpser connects to a telnet service, tcpser will negotiate the connection 98 | using the telnet protocol. If telnet is detected, then tcpser will support 99 | RFC 856 (Telnet Binary Transmission), so that 8-bit file transfers will 100 | work correctly. 101 | 102 | tcpser can be configured to support multiple serial/ip232 ports on one TCP/IP 103 | port. Simply repeat the -s and -d/-v parameters on the command line for each 104 | serial/ip232 port to be configured. Options s,S,a,A,c,C,I, and T will 105 | "propagate" to subsequent connections, unless they are redefined. Defaults 106 | for s and S are 38400. This configuration enables the operation of a 107 | multi-line BBS on one TCP/IP port. 108 | 109 | Frequently used addresses can be configured in the "phonebook", like so: 110 | ``` 111 | tcpser .... -nhome=jbrain.com:6400 112 | ``` 113 | This is also useful for systems that do not accept non-numeric phone numbers: 114 | ``` 115 | tcpser .... -n9169651701=bbs.fozztexx.com 116 | ``` 117 | One can even "hide" a regular IP address or DNS entry by aliasing it to 118 | something else: 119 | ``` 120 | tcpser .... -njbrain.com=bestbbs.com 121 | ``` 122 | At this point, phonebook support is very alpha, so use with care. 123 | 124 | ## Emulation 125 | 126 | All of the standard Hayes commands should behave as expected. Some of 127 | of the proprietary commands are not implemented, but should not cause 128 | errors. 129 | 130 | Examples: 131 | 132 | | Command | Effect | 133 | |---------------------|----------------------------------------------------| 134 | | ats0=1 | set number of rings to answer | 135 | | ata | answer the line | 136 | | ath0 | hang up | 137 | | ats12? | query S register 12 | 138 | | ate0 | turn off echo | 139 | | at&k3 | set flow control to RTS/CTS | 140 | | atdtjbrain.com:6400 | "dial" jbrain.com, port 6400 (defaults to port 23) | 141 | | atdl | "dial" last number | 142 | | a/ | repeat last command | 143 | 144 | Commands can be chained, as on a regular modem: 145 | ``` 146 | ats0=1z&c1&k3%f0s3=13dtjbrain.com 147 | ``` 148 | tcpser supports the Hayes break sequence semantics, so +++ should operate 149 | correctly, even if the sequence of characters is used in normal data 150 | transmissions. 151 | 152 | ## Cable 153 | 154 | tcpser can be used with a regular null modem cable, but it utilizes the DTR 155 | line on the PC serial port to reflect the state of the DCD line as seen by the 156 | target system. On a normal null-modem cable, DTR is mapped to DCD/DSR, which 157 | implies DSR will also reflect the state of DCD on the target machine. However, 158 | some systems (notably those utilizing the 6551 ACIA communication IC) will not 159 | transmit unless DSR is held high. In this case, a quick qorkaround is to force 160 | DCD to be held high by adding `-i"&c0"` to the tcpser parameter list. However, 161 | this also prevents normal operation of the DCD line, which is needed by some 162 | BBS systems. A more permanent solution is to construct a modified null-modem 163 | cable or modify an existing cable to the following specifications: 164 | 165 | PC Target 166 | 167 | CTS-----RTS 168 | RTS-----CTS 169 | SND-----RCV 170 | RCV-----SND 171 | 172 | DTR-----DCD 173 | 174 | DCD-+-+-DTR 175 | | | 176 | DSR-+ +-DSR 177 | 178 | GND-----GND 179 | 180 | This differs from a regular null-modem cable in that the target machine has DSR 181 | looped to DTR, not to DCD. Note that this cable is directional. 182 | 183 | Normally, the target machine will configure DSR to float to a high state if 184 | unconnected. As well, PCs do not require a valid DSR line for operation. Thus, 185 | a simpler cable can be constructed that is bi-directional: 186 | 187 | CTS-----RTS 188 | RTS-----CTS 189 | SND-----RCV 190 | RCV-----SND 191 | DTR-----DCD 192 | DCD-----DTR 193 | GND-----GND 194 | 195 | Unless there are issues, we recommend this simplified version, as it can be 196 | installed in either direction. 197 | 198 | As an even simpler solution, many have simply taken a normal rs232 DE-9F to 199 | DE-9M cable and removed pin 6 from the male end (DSR). This is fine, but the 200 | cable must be installed between the null modem adapter and the target machine: 201 | 202 | PC ----- null-modem adapter ----- cable with pin 6 removed ------ target machine 203 | 204 | Any other configuration will not work correctly. 205 | 206 | ## Platform notes 207 | 208 | Win32 users should use /dev/ttyS0-3 for COM1-4. At present, using "com1:" 209 | does not operate correctly. 210 | 211 | Raymond Day sends the following Ubuntu 7.10 autorun scripts: 212 | 213 | In: 214 | 215 | /etc/init.d/ 216 | 217 | Make a file named something like tcpser with this code in it: 218 | ``` 219 | #!/bin/sh 220 | # Start tcpser at 1200 boud on both RS232 ports for Q-Link Reloaded. 221 | case "$1" in 222 | 'start') 223 | tcpser -d /dev/ttyS0 -s 1200 -i"e0&k0&c0" -n"5551212"=66.135.39.36:5190& 224 | tcpser -d /dev/ttyS1 -p 6401 -s 1200 -i"e0&k0&c0" -n"5551212"=66.135.39.36:5190& 225 | ;; 226 | 'stop') 227 | ;; 228 | *) 229 | echo "Usage: $0 { start | stop }" 230 | ;; 231 | esac 232 | exit 0 233 | ``` 234 | This has been tested on the following platforms: 235 | 236 | * Linux 2.4.20-8 237 | * Windows XP 238 | * Windows XP SP1 239 | * Slackware 10.0 240 | 241 | Help: 242 | 243 | tcpser has a small but active user community. Help can be found by asking a 244 | question in comp.sys.cbm, on the NEWNet #c64friends IRC channel, or by emailing 245 | the author. 246 | 247 | --- 248 | 249 | Jim Brain 250 | brain@jbrain.com 251 | www.jbrain.com 252 | 253 | 254 | The ip232 support was added by Anthony Tolle. For questions regarding that, 255 | e-mail atolle@gcns.com 256 | 257 | -------------------------------------------------------------------------------- /bridge: -------------------------------------------------------------------------------- 1 | tcpser-1.0rc10/tcpser -d /dev/ttyS1 -s 1200 -i"e0" -tsS -n"5551212=ltshosting.net:5190" 2 | 3 | -------------------------------------------------------------------------------- /src/bridge.c: -------------------------------------------------------------------------------- 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 "serial.h" 16 | #include "getcmd.h" 17 | #include "bridge.h" 18 | 19 | const char MDM_NO_ANSWER[] = "NO ANSWER\n"; 20 | 21 | //const char CONNECT_NOTICE[] = "\r\nCONNECTING...\r\n"; 22 | 23 | //const char TELNET_NOTICE[] = "TELNET MODE ENABLED\r\n"; 24 | 25 | int accept_connection(modem_config *cfg) 26 | { 27 | LOG_ENTER(); 28 | 29 | cfg->line_data.fd = ip_accept(cfg->line_data.sfd); 30 | if (cfg->line_data.fd > -1) { 31 | cfg->line_data.valid_conn = TRUE; 32 | if (cfg->data.direct_conn == TRUE) { 33 | cfg->conn_type = MDM_CONN_INCOMING; 34 | mdm_off_hook(cfg); 35 | cfg->cmd_mode = TRUE; 36 | } 37 | else { 38 | //line_write(cfg,(char*)CONNECT_NOTICE,strlen(CONNECT_NOTICE)); 39 | cfg->rings = 0; 40 | mdm_send_ring(cfg); 41 | } 42 | // tell parent I got it. 43 | LOG(LOG_DEBUG, "Informing parent task that I am busy"); 44 | writePipe(cfg->data.mp[0][1], MSG_ACCEPTED); 45 | } 46 | LOG_EXIT(); 47 | return 0; 48 | } 49 | 50 | int parse_ip_data(modem_config *cfg, unsigned char *data, int len) 51 | { 52 | // I'm going to cheat and assume it comes in chunks. 53 | int i = 0; 54 | int ch; 55 | unsigned char text[1025]; 56 | int text_len = 0; 57 | 58 | 59 | if (cfg->line_data.first_char == TRUE) { 60 | cfg->line_data.first_char = FALSE; 61 | if ((data[0] == 0xff) || (data[0] == 0x1a)) { 62 | //line_write(cfg,(char*)TELNET_NOTICE,strlen(TELNET_NOTICE)); 63 | LOG(LOG_INFO, "Detected telnet"); 64 | cfg->line_data.is_telnet = TRUE; 65 | } 66 | } 67 | 68 | if (cfg->line_data.is_telnet == TRUE) { 69 | while (i < len) { 70 | ch = data[i]; 71 | if (NVT_IAC == ch) { 72 | // what if we roll off the end? 73 | ch = data[i + 1]; 74 | switch (ch) { 75 | case NVT_WILL: 76 | case NVT_DO: 77 | case NVT_WONT: 78 | case NVT_DONT: 79 | /// again, overflow issues... 80 | LOG(LOG_INFO, "Parsing nvt command"); 81 | parse_nvt_command(cfg->line_data.fd, &cfg->line_data.nvt_data, ch, 82 | data[i + 2], cfg->parity); 83 | i += 3; 84 | break; 85 | case NVT_SB: // sub negotiation 86 | // again, overflow... 87 | i += 88 | parse_nvt_subcommand(cfg->line_data.fd, &cfg->line_data.nvt_data, 89 | data + i, len - i, cfg->dce_speed); 90 | break; 91 | case NVT_IAC: 92 | if (cfg->line_data.nvt_data.binary_recv) 93 | text[text_len++] = NVT_IAC; 94 | // fall through to skip this sequence 95 | default: 96 | // ignore... 97 | i += 2; 98 | } 99 | } 100 | else { 101 | text[text_len++] = data[i++]; 102 | } 103 | if (text_len == 1024) { 104 | text[text_len] = 0; 105 | // write to serial... 106 | mdm_write(cfg, text, text_len); 107 | text_len = 0; 108 | } 109 | } 110 | if (text_len) { 111 | text[text_len] = 0; 112 | // write to serial... 113 | mdm_write(cfg, text, text_len); 114 | } 115 | } 116 | else { 117 | mdm_write(cfg, data, len); 118 | } 119 | return 0; 120 | } 121 | 122 | void *ip_thread(void *arg) 123 | { 124 | modem_config *cfg = (modem_config *) arg; 125 | 126 | int action_pending = FALSE; 127 | fd_set readfs; 128 | int max_fd; 129 | int res = 0; 130 | unsigned char buf[256]; 131 | int rc; 132 | 133 | 134 | LOG_ENTER(); 135 | while (TRUE) { 136 | FD_ZERO(&readfs); 137 | FD_SET(cfg->data.cp[1][0], &readfs); 138 | max_fd = cfg->data.cp[1][0]; 139 | if (action_pending == FALSE 140 | && cfg->conn_type != MDM_CONN_NONE 141 | && cfg->cmd_mode == FALSE 142 | && cfg->line_data.fd > -1 && cfg->line_data.valid_conn == TRUE) { 143 | FD_SET(cfg->line_data.fd, &readfs); 144 | max_fd = MAX(max_fd, cfg->line_data.fd); 145 | } 146 | max_fd++; 147 | rc = select(max_fd, &readfs, NULL, NULL, NULL); 148 | if (rc == -1) { 149 | ELOG(LOG_WARN, "Select returned error"); 150 | // handle error 151 | } 152 | else { 153 | // we got data 154 | if (cfg->line_data.valid_conn == TRUE && FD_ISSET(cfg->line_data.fd, &readfs)) { // socket 155 | LOG(LOG_DEBUG, "Data available on socket"); 156 | res = recv(cfg->line_data.fd, buf, sizeof(buf), 0); 157 | if (0 >= res) { 158 | LOG(LOG_INFO, "No socket data read, assume closed peer"); 159 | writePipe(cfg->data.cp[0][1], MSG_DISCONNECT); 160 | action_pending = TRUE; 161 | } 162 | else { 163 | LOG(LOG_DEBUG, "Read %d bytes from socket", res); 164 | buf[res] = 0; 165 | log_trace(TRACE_IP_IN, (char *) buf, res); 166 | parse_ip_data(cfg, buf, res); 167 | } 168 | } 169 | if (FD_ISSET(cfg->data.cp[1][0], &readfs)) { // pipe 170 | 171 | res = read(cfg->data.cp[1][0], buf, sizeof(buf) - 1); 172 | LOG(LOG_DEBUG, "IP thread notified"); 173 | action_pending = FALSE; 174 | } 175 | } 176 | } 177 | LOG_EXIT(); 178 | } 179 | 180 | void *ctrl_thread(void *arg) 181 | { 182 | modem_config *cfg = (modem_config *) arg; 183 | int status; 184 | int new_status; 185 | 186 | 187 | LOG_ENTER(); 188 | 189 | status = dce_get_control_lines(cfg); 190 | while (status > -1) { 191 | new_status = dce_check_control_lines(cfg); 192 | if (new_status > -1 && status != new_status) { 193 | // something changed 194 | if ((status & MDM_CL_DTR_HIGH) != (new_status & MDM_CL_DTR_HIGH)) { 195 | if ((new_status & MDM_CL_DTR_HIGH) == 0) { 196 | LOG(LOG_INFO, "DTR has gone low"); 197 | writePipe(cfg->data.wp[0][1], MSG_DTR_DOWN); 198 | } 199 | else { 200 | LOG(LOG_INFO, "DTR has gone high"); 201 | writePipe(cfg->data.wp[0][1], MSG_DTR_UP); 202 | } 203 | } 204 | } 205 | status = new_status; 206 | } 207 | LOG_EXIT(); 208 | // need to quit application, as status cannot be obtained. 209 | exit(-1); 210 | } 211 | 212 | int spawn_ctrl_thread(modem_config *cfg) 213 | { 214 | int rc; 215 | pthread_t thread_id; 216 | 217 | rc = pthread_create(&thread_id, NULL, ctrl_thread, (void *) cfg); 218 | LOG(LOG_ALL, "CTRL thread ID=%d", (int) thread_id); 219 | 220 | if (rc < 0) { 221 | ELOG(LOG_FATAL, "CTRL thread could not be started"); 222 | exit(-1); 223 | } 224 | return 0; 225 | } 226 | 227 | int spawn_ip_thread(modem_config *cfg) 228 | { 229 | int rc; 230 | pthread_t thread_id; 231 | 232 | 233 | rc = pthread_create(&thread_id, NULL, ip_thread, (void *) cfg); 234 | LOG(LOG_ALL, "IP thread ID=%d", (int) thread_id); 235 | 236 | if (rc < 0) { 237 | ELOG(LOG_FATAL, "IP thread could not be started"); 238 | exit(-1); 239 | } 240 | return 0; 241 | } 242 | 243 | void *run_bridge(void *arg) 244 | { 245 | modem_config *cfg = (modem_config *) arg; 246 | struct timeval timer; 247 | struct timeval *ptimer; 248 | int max_fd = 0; 249 | fd_set readfs; 250 | int res = 0; 251 | unsigned char buf[256]; 252 | int rc = 0; 253 | 254 | int last_conn_type; 255 | int last_cmd_mode = cfg->cmd_mode; 256 | 257 | 258 | LOG_ENTER(); 259 | 260 | if (-1 == pipe(cfg->data.wp[0])) { 261 | ELOG(LOG_FATAL, "Control line watch task incoming IPC pipe could not be created"); 262 | exit(-1); 263 | } 264 | if (-1 == pipe(cfg->data.cp[0])) { 265 | ELOG(LOG_FATAL, "IP thread incoming IPC pipe could not be created"); 266 | exit(-1); 267 | } 268 | if (-1 == pipe(cfg->data.cp[1])) { 269 | ELOG(LOG_FATAL, "IP thread outgoing IPC pipe could not be created"); 270 | exit(-1); 271 | } 272 | 273 | spawn_ctrl_thread(cfg); 274 | spawn_ip_thread(cfg); 275 | 276 | mdm_set_control_lines(cfg); 277 | strncpy(cfg->cur_line, cfg->config0, sizeof(cfg->cur_line)); 278 | last_conn_type = cfg->conn_type; 279 | last_cmd_mode = cfg->cmd_mode; 280 | cfg->allow_transmit = FALSE; 281 | // call some functions behind the scenes 282 | mdm_disconnect(cfg); 283 | mdm_parse_cmd(cfg); 284 | // if direct connection, and num length > 0, dial number. 285 | if (cfg->data.direct_conn == TRUE) { 286 | if (strlen(cfg->data.direct_conn_num) > 0 && cfg->data.direct_conn_num[0] != ':') { 287 | // we have a direct number to connect to. 288 | strncpy(cfg->dialno, cfg->data.direct_conn_num, sizeof(cfg->dialno)); 289 | if (0 != line_connect(cfg)) { 290 | LOG(LOG_FATAL, "Cannot connect to Direct line address!"); 291 | // probably should exit... 292 | exit(-1); 293 | } 294 | else { 295 | cfg->conn_type = MDM_CONN_OUTGOING; 296 | } 297 | 298 | } 299 | } 300 | cfg->allow_transmit = TRUE; 301 | for (;;) { 302 | if (last_conn_type != cfg->conn_type) { 303 | LOG(LOG_ALL, "Connection status change, handling"); 304 | //writePipe(cfg->data.mp[0][1],MSG_NOTIFY); 305 | writePipe(cfg->data.cp[1][1], MSG_NOTIFY); 306 | if (cfg->conn_type == MDM_CONN_OUTGOING) { 307 | if (strlen(cfg->data.local_connect) > 0) { 308 | writeFile(cfg->data.local_connect, cfg->line_data.fd); 309 | } 310 | if (strlen(cfg->data.remote_connect) > 0) { 311 | writeFile(cfg->data.remote_connect, cfg->line_data.fd); 312 | } 313 | } 314 | else if (cfg->conn_type == MDM_CONN_INCOMING) { 315 | if (strlen(cfg->data.local_answer) > 0) { 316 | writeFile(cfg->data.local_answer, cfg->line_data.fd); 317 | } 318 | if (strlen(cfg->data.remote_answer) > 0) { 319 | writeFile(cfg->data.remote_answer, cfg->line_data.fd); 320 | } 321 | } 322 | last_conn_type = cfg->conn_type; 323 | } 324 | if (last_cmd_mode != cfg->cmd_mode) { 325 | writePipe(cfg->data.cp[1][1], MSG_NOTIFY); 326 | last_cmd_mode = cfg->cmd_mode; 327 | } 328 | LOG(LOG_ALL, "Waiting for modem/control line/timer/socket activity"); 329 | LOG(LOG_ALL, "Command Mode=%d, Connection status=%d", cfg->cmd_mode, cfg->conn_type); 330 | max_fd = MAX(cfg->data.mp[1][0], cfg->dce_data.fd); 331 | max_fd = MAX(max_fd, cfg->data.wp[0][0]); 332 | max_fd = MAX(max_fd, cfg->data.cp[0][0]); 333 | FD_ZERO(&readfs); 334 | FD_SET(cfg->data.mp[1][0], &readfs); 335 | FD_SET(cfg->dce_data.fd, &readfs); 336 | FD_SET(cfg->data.wp[0][0], &readfs); 337 | FD_SET(cfg->data.cp[0][0], &readfs); 338 | ptimer = NULL; 339 | if (cfg->cmd_mode == FALSE) { 340 | if (cfg->pre_break_delay == FALSE || cfg->break_len == 3) { 341 | long long usec; 342 | 343 | 344 | LOG(LOG_ALL, "Setting timer for break delay"); 345 | usec = cfg->s[SRegisterGuardTime] * 20000; 346 | timer.tv_sec = usec / 1000000; 347 | timer.tv_usec = usec % 1000000; 348 | ptimer = &timer; 349 | } 350 | else if (cfg->pre_break_delay == TRUE && cfg->break_len > 0) { 351 | LOG(LOG_ALL, "Setting timer for inter-break character delay"); 352 | timer.tv_sec = 1; // 1 second 353 | timer.tv_usec = 0; 354 | ptimer = &timer; 355 | } 356 | else if (cfg->s[30] != 0) { 357 | LOG(LOG_ALL, "Setting timer for inactivity delay"); 358 | timer.tv_sec = cfg->s[30] * 10; 359 | timer.tv_usec = 0; 360 | ptimer = &timer; 361 | } 362 | } 363 | else if (cfg->cmd_mode == TRUE && cfg->conn_type == MDM_CONN_NONE 364 | && cfg->line_data.valid_conn == TRUE) { 365 | LOG(LOG_ALL, "Setting timer for rings"); 366 | timer.tv_sec = 4; 367 | timer.tv_usec = 0; 368 | ptimer = &timer; 369 | } 370 | max_fd++; 371 | rc = select(max_fd, &readfs, NULL, NULL, ptimer); 372 | if (rc == -1) { 373 | ELOG(LOG_WARN, "Select returned error"); 374 | // handle error 375 | } 376 | else if (rc == 0) { 377 | // timer popped. 378 | if (cfg->cmd_mode == TRUE && cfg->conn_type == MDM_CONN_NONE 379 | && cfg->line_data.valid_conn == TRUE) { 380 | if (cfg->s[0] == 0 && cfg->rings == 10) { 381 | // not going to answer, send some data back to IP and disconnect. 382 | if (strlen(cfg->data.no_answer) == 0) { 383 | line_write(cfg, (char *) MDM_NO_ANSWER, strlen(MDM_NO_ANSWER)); 384 | } 385 | else { 386 | writeFile(cfg->data.no_answer, cfg->line_data.fd); 387 | } 388 | mdm_disconnect(cfg); 389 | } 390 | else 391 | mdm_send_ring(cfg); 392 | } 393 | else 394 | mdm_handle_timeout(cfg); 395 | } 396 | if (FD_ISSET(cfg->dce_data.fd, &readfs)) { // serial port 397 | LOG(LOG_DEBUG, "Data available on serial port"); 398 | res = dce_read(cfg, buf, sizeof(buf)); 399 | LOG(LOG_DEBUG, "Read %d bytes from serial port", res); 400 | if (res > 0) { 401 | if (cfg->conn_type == MDM_CONN_NONE && cfg->off_hook == TRUE) { 402 | // this handles the case where atdt goes off hook, but no 403 | // connection 404 | mdm_disconnect(cfg); 405 | mdm_send_response(MDM_RESP_OK, cfg); 406 | } 407 | else { 408 | mdm_parse_data(cfg, (char *) buf, res); 409 | } 410 | } 411 | 412 | } 413 | if (FD_ISSET(cfg->data.wp[0][0], &readfs)) { // control pipe 414 | res = read(cfg->data.wp[0][0], buf, sizeof(buf) - 1); 415 | buf[res] = 0; 416 | LOG(LOG_DEBUG, "Received %c from Control line watch task", buf[0]); 417 | switch (buf[0]) { 418 | case MSG_DTR_DOWN: 419 | // DTR drop, close any active connection and put 420 | // in cmd_mode 421 | mdm_disconnect(cfg); 422 | break; 423 | case MSG_DTR_UP: 424 | break; 425 | } 426 | } 427 | if (FD_ISSET(cfg->data.cp[0][0], &readfs)) { // ip thread pipe 428 | res = read(cfg->data.cp[0][0], buf, sizeof(buf)); 429 | LOG(LOG_DEBUG, "Received %c from ip thread", buf[0]); 430 | switch (buf[0]) { 431 | case MSG_DISCONNECT: 432 | if (cfg->data.direct_conn == TRUE) { 433 | // what should we do here... 434 | LOG(LOG_ERROR, 435 | "Direct Connection Link broken, disconnecting and awaiting new direct connection"); 436 | cfg->data.direct_conn = FALSE; 437 | mdm_disconnect(cfg); 438 | cfg->data.direct_conn = TRUE; 439 | } 440 | else { 441 | mdm_disconnect(cfg); 442 | } 443 | break; 444 | } 445 | } 446 | if (FD_ISSET(cfg->data.mp[1][0], &readfs)) { // parent pipe 447 | LOG(LOG_DEBUG, "Data available on incoming IPC pipe"); 448 | res = read(cfg->data.mp[1][0], buf, sizeof(buf)); 449 | switch (buf[0]) { 450 | case MSG_ACCEPT: // accept connection. 451 | accept_connection(cfg); 452 | break; 453 | } 454 | } 455 | } 456 | LOG_EXIT(); 457 | } 458 | -------------------------------------------------------------------------------- /src/bridge.h: -------------------------------------------------------------------------------- 1 | #define MSG_NOT_ACTIVE 'a' 2 | #define MSG_ACTIVE 'A' 3 | #define MSG_NOT_LISTENING 'l' 4 | #define MSG_LISTENING 'L' 5 | #define MSG_ACCEPT '+' 6 | #define MSG_ACCEPTED '+' 7 | #define MSG_DTR_DOWN 'd' 8 | #define MSG_DTR_UP 'D' 9 | #define MSG_RTS_DOWN 'r' 10 | #define MSG_RTS_UP 'R' 11 | #define MSG_CD_DOWN 'c' 12 | #define MSG_CD_UP 'c' 13 | #define MSG_RNG_DOWN 'n' 14 | #define MSG_RNG_UP 'N' 15 | #define MSG_DISCONNECT 'D' 16 | #define MSG_NOTIFY 'N' 17 | 18 | int accept_connection(modem_config *); 19 | int parse_ip_data(modem_config *cfg, unsigned char *data, int len); 20 | void *run_bridge(void *arg); 21 | -------------------------------------------------------------------------------- /src/dce.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "debug.h" 6 | #include "serial.h" 7 | #include "modem_core.h" 8 | #include "ip232.h" // needs modem_core.h 9 | #include "dce.h" 10 | 11 | int dce_init_config(modem_config *cfg) 12 | { 13 | return 0; 14 | } 15 | 16 | int dce_init_conn(modem_config *cfg) 17 | { 18 | int rc; 19 | 20 | 21 | LOG_ENTER(); 22 | if (cfg->dce_data.is_ip232) { 23 | rc = ip232_init_conn(cfg); 24 | } 25 | else { 26 | rc = ser_init_conn(cfg->dce_data.tty, cfg->dte_speed); 27 | cfg->dce_data.fd = rc; 28 | } 29 | 30 | LOG_EXIT(); 31 | return rc; 32 | } 33 | 34 | int dce_set_flow_control(modem_config *cfg, int opts) 35 | { 36 | int status = 0; 37 | int rc = 0; 38 | 39 | 40 | LOG_ENTER(); 41 | if (opts == 0) { 42 | LOG(LOG_ALL, "Setting NONE flow control"); 43 | } 44 | else { 45 | if ((opts & MDM_FC_RTS) != 0) { 46 | LOG(LOG_ALL, "Setting RTSCTS flow control"); 47 | status |= CRTSCTS; 48 | } 49 | if ((opts & MDM_FC_XON) != 0) { 50 | status |= (IXON | IXOFF); 51 | LOG(LOG_ALL, "Setting XON/XOFF flow control"); 52 | } 53 | } 54 | 55 | if (cfg->dce_data.is_ip232) { 56 | rc = ip232_set_flow_control(cfg, status); 57 | } 58 | else { 59 | rc = ser_set_flow_control(cfg->dce_data.fd, status); 60 | } 61 | 62 | LOG_EXIT() 63 | return rc; 64 | } 65 | 66 | int dce_set_control_lines(modem_config *cfg, int state) 67 | { 68 | int status = 0; 69 | int rc; 70 | 71 | 72 | LOG_ENTER(); 73 | if ((state & MDM_CL_CTS_HIGH) != 0) { 74 | LOG(LOG_ALL, "Setting CTS pin high"); 75 | status |= TIOCM_RTS; 76 | } 77 | else { 78 | LOG(LOG_ALL, "Setting CTS pin low"); 79 | //status &= ~TIOCM_RTS; 80 | } 81 | if ((state & MDM_CL_DCD_HIGH) != 0) { 82 | LOG(LOG_ALL, "Setting DCD pin high"); 83 | status |= TIOCM_DTR; 84 | } 85 | else { 86 | LOG(LOG_ALL, "Setting DCD pin low"); 87 | //status &= ~TIOCM_DTR; 88 | } 89 | 90 | if (cfg->dce_data.is_ip232) { 91 | rc = ip232_set_control_lines(cfg, status); 92 | } 93 | else { 94 | rc = ser_set_control_lines(cfg->dce_data.fd, status); 95 | } 96 | 97 | LOG_EXIT(); 98 | return rc; 99 | } 100 | 101 | int dce_get_control_lines(modem_config *cfg) 102 | { 103 | int status; 104 | int rc_status; 105 | 106 | 107 | if (cfg->dce_data.is_ip232) { 108 | status = ip232_get_control_lines(cfg); 109 | } 110 | else { 111 | status = ser_get_control_lines(cfg->dce_data.fd); 112 | } 113 | 114 | if (status > -1) { 115 | rc_status = ((status & TIOCM_DSR) != 0 ? MDM_CL_DTR_HIGH : 0); 116 | } 117 | else { 118 | rc_status = status; 119 | } 120 | 121 | return rc_status; 122 | } 123 | 124 | int dce_check_control_lines(modem_config *cfg) 125 | { 126 | int status = 0; 127 | int new_status = 0; 128 | 129 | 130 | LOG_ENTER(); 131 | status = dce_get_control_lines(cfg); 132 | new_status = status; 133 | while (new_status > -1 && status == new_status) { 134 | usleep(100000); 135 | new_status = dce_get_control_lines(cfg); 136 | } 137 | 138 | LOG_EXIT(); 139 | return new_status; 140 | } 141 | 142 | int dce_write(modem_config *cfg, unsigned char data[], int len) 143 | { 144 | if (cfg->dce_data.is_ip232) { 145 | return ip232_write(cfg, data, len); 146 | } 147 | return ser_write(cfg->dce_data.fd, data, len); 148 | } 149 | 150 | int dce_read(modem_config *cfg, unsigned char data[], int len) 151 | { 152 | if (cfg->dce_data.is_ip232) { 153 | return ip232_read(cfg, data, len); 154 | } 155 | return ser_read(cfg->dce_data.fd, data, len); 156 | } 157 | -------------------------------------------------------------------------------- /src/dce.h: -------------------------------------------------------------------------------- 1 | #ifndef DCE_H 2 | #define DCE_H 1 3 | 4 | int dce_init(void); 5 | int dce_init_config(modem_config *cfg); 6 | int dce_init_conn(modem_config *cfg); 7 | int dce_set_flow_control(modem_config *cfg, int opts); 8 | int dce_set_control_lines(modem_config *cfg, int state); 9 | int dce_get_control_lines(modem_config *cfg); 10 | int dce_check_control_lines(modem_config *cfg); 11 | int dce_read(modem_config *cfg, unsigned char *data, int len); 12 | int dce_write(modem_config *cfg, unsigned char *data, int len); 13 | 14 | //int dce_check_for_break(modem_config *cfg, char ch, int chars_left); 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /src/debug.c: -------------------------------------------------------------------------------- 1 | #include // for exit... 2 | #include 3 | #include 4 | #include 5 | #define DEBUG_VARS 1 // need this so we don't get extern defs 6 | #include "debug.h" 7 | 8 | int log_level = 0; 9 | FILE *log_file; 10 | int trace_flags = 0; 11 | char *trace_type[9]; // cheesy, but I can't think of another o(1) way 12 | char *log_desc[LOG_TRACE + 1]; 13 | pthread_mutex_t log_mutex; 14 | 15 | int log_init() 16 | { 17 | log_file = stdout; 18 | log_level = 0; 19 | trace_flags = 0; 20 | trace_type[TRACE_MODEM_IN] = "RS<-"; 21 | trace_type[TRACE_MODEM_OUT] = "RS->"; 22 | trace_type[TRACE_IP_IN] = "IP<-"; 23 | trace_type[TRACE_IP_OUT] = "IP->"; 24 | log_desc[LOG_FATAL] = "FATAL"; 25 | log_desc[LOG_ERROR] = "ERROR"; 26 | log_desc[LOG_WARN] = "WARN"; 27 | log_desc[LOG_INFO] = "INFO"; 28 | log_desc[LOG_DEBUG] = "DEBUG"; 29 | log_desc[LOG_ENTER_EXIT] = "ENTER_EXIT"; 30 | log_desc[LOG_ALL] = "DEBUG_X"; 31 | log_desc[LOG_TRACE] = ""; 32 | if (-1 == pthread_mutex_init(&log_mutex, NULL)) { 33 | perror("Could not create Log Mutex"); 34 | exit(-1); 35 | } 36 | return 0; 37 | } 38 | 39 | void log_set_file(FILE * a) 40 | { 41 | log_file = a; 42 | } 43 | 44 | void log_set_level(int a) 45 | { 46 | log_level = a; 47 | } 48 | 49 | void log_set_trace_flags(int a) 50 | { 51 | trace_flags = a; 52 | } 53 | 54 | int log_get_trace_flags() 55 | { 56 | return trace_flags; 57 | } 58 | 59 | void log_trace(int type, char *line, int len) 60 | { 61 | int i = 0; 62 | unsigned int ch; 63 | char data[64] = "\0"; 64 | char *dptr = NULL; 65 | char text[17]; 66 | 67 | 68 | if (len == 0) 69 | return; 70 | 71 | if ((type & trace_flags) != 0) { 72 | text[16] = 0; 73 | for (i = 0; i < len; i++) { 74 | if ((i % 16) == 0) { 75 | // beginning of line 76 | dptr = data; 77 | sprintf(dptr, "%4.4x|", i); 78 | } 79 | ch = ((unsigned char *) line)[i]; 80 | sprintf(dptr + 5 + ((i % 16) * 3), "%2.2x", ch); 81 | if (ch > 31 && ch < 127) { 82 | text[i % 16] = ch; 83 | } 84 | else { 85 | text[i % 16] = '.'; 86 | } 87 | if ((i % 16) == 15) { 88 | log_start(LOG_TRACE); 89 | fprintf(log_file, "%s|%s|%s|", trace_type[type], data, text); 90 | log_end(); 91 | } 92 | else { 93 | sprintf(dptr + 7 + ((i % 16) * 3), " "); 94 | } 95 | } 96 | i = i % 16; 97 | if (i > 0) { 98 | for (; i < 16; i++) { 99 | sprintf(dptr + 5 + ((i % 16) * 3), " "); 100 | if ((i % 16) != 15) { 101 | sprintf(dptr + 7 + ((i % 16) * 3), " "); 102 | } 103 | text[i % 16] = ' '; 104 | } 105 | log_start(LOG_TRACE); 106 | fprintf(log_file, "%s|%s|%s|", trace_type[type], data, text); 107 | } 108 | log_end(); 109 | } 110 | } 111 | 112 | void log_start(int level) 113 | { 114 | char t[23]; 115 | time_t now; 116 | 117 | 118 | if (-1 == pthread_mutex_lock(&log_mutex)) { 119 | perror("Could not lock the log mutex"); 120 | } 121 | else { 122 | // we have the lock. 123 | now = time(NULL); 124 | strftime(t, 22, "%Y-%m-%d %H:%M:%S", localtime(&now)); 125 | fprintf(log_file, "%s:%5.5d:%s:", t, (int) pthread_self(), log_desc[level]); 126 | //free(t); 127 | } 128 | } 129 | 130 | void log_end() 131 | { 132 | fprintf(log_file, "\n"); 133 | fflush(log_file); 134 | if (-1 == pthread_mutex_unlock(&log_mutex)) { 135 | perror("Could not lock the log mutex"); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/debug.h: -------------------------------------------------------------------------------- 1 | #ifndef DEBUG_H 2 | #define DEBUG_H 1 3 | 4 | #define LOG_TRACE 10 5 | #define LOG_ALL 7 6 | #define LOG_ENTER_EXIT 6 7 | #define LOG_DEBUG 5 8 | #define LOG_INFO 4 9 | #define LOG_WARN 3 10 | #define LOG_ERROR 2 11 | #define LOG_FATAL 1 12 | #define LOG_NONE 0 13 | 14 | #define TRACE_MODEM_IN 1 15 | #define TRACE_MODEM_OUT 2 16 | #define TRACE_IP_IN 4 17 | #define TRACE_IP_OUT 8 18 | 19 | #include // needed for strerror 20 | #include // needed for strerror 21 | #include // needed for errno 22 | 23 | #if __STDC_VERSION__ < 199901L 24 | #if __GNUC__ >= 2 25 | #define __func__ __FUNCTION__ 26 | #else 27 | #define __func__ "" 28 | #endif 29 | #endif 30 | 31 | #define LOG(a,args...) do { \ 32 | if(a <= log_level) { \ 33 | log_start(a); \ 34 | fprintf(log_file,args); \ 35 | log_end(); \ 36 | } \ 37 | } while(0) 38 | 39 | #define ELOG(a,args...) do { \ 40 | if(a <= log_level) { \ 41 | log_start(a); \ 42 | fprintf(log_file,args); \ 43 | fprintf(log_file," (%s)\n",strerror(errno)); \ 44 | log_end(); \ 45 | } \ 46 | } while(0) 47 | 48 | #define LOG_ENTER() LOG(LOG_ENTER_EXIT,"Entering %s function",__func__); 49 | #define LOG_EXIT() LOG(LOG_ENTER_EXIT,"Exitting %s function",__func__); 50 | int log_init(void); 51 | void log_set_file(FILE * a); 52 | void log_set_level(int a); 53 | int log_get_trace_flags(); 54 | void log_set_trace_flags(int a); 55 | void log_trace(int type, char *line, int len); 56 | void log_start(int level); 57 | void log_end(); 58 | 59 | #endif 60 | #ifndef DEBUG_VARS 61 | #define DEBUG_VARS 1 62 | 63 | #include 64 | 65 | extern int log_level; 66 | extern FILE *log_file; 67 | 68 | #endif 69 | -------------------------------------------------------------------------------- /src/getcmd.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "getcmd.h" 6 | 7 | int getData(char line[], int *index, int len, int *data_start, int *data_end, 8 | int complex_parse) 9 | { 10 | int alpha = FALSE; 11 | int done = FALSE; 12 | 13 | 14 | *data_start = *index; 15 | 16 | while (*index < len && done != TRUE) { 17 | // I'm going to assume either 18 | // a number 19 | // a string with a space 20 | switch (line[*index]) { 21 | case ' ': 22 | if (!complex_parse && *index != *data_start) { 23 | // leave space, next call will skip it. 24 | done = TRUE; 25 | } 26 | else if (*index != *data_start) { 27 | // we are complex, add the space and continue. 28 | (*index)++; 29 | } 30 | else { 31 | // we have not started, eat space and continue. 32 | (*index)++; 33 | *data_start = *index; 34 | } 35 | break; 36 | case 0: 37 | done = TRUE; 38 | break; 39 | case '0': 40 | case '1': 41 | case '2': 42 | case '3': 43 | case '4': 44 | case '5': 45 | case '6': 46 | case '7': 47 | case '8': 48 | case '9': // isnum 49 | (*index)++; 50 | break; 51 | default: 52 | if (!complex_parse && *index != *data_start && 0 == alpha) { 53 | // we were a number, but we've hit an alpha 'S0=137S...' 54 | done = TRUE; 55 | } 56 | else { 57 | (*index)++; 58 | alpha = TRUE; 59 | } 60 | break; 61 | } 62 | } 63 | *data_end = (*index); 64 | return 0; 65 | } 66 | 67 | int getNumber(char line[], int *index, int len) 68 | { 69 | int num = 0; 70 | int found = FALSE; 71 | 72 | 73 | while (*index < len && 0 != isdigit(line[*index])) { 74 | num = num * 10 + line[(*index)++] - '0'; 75 | found = 1; 76 | } 77 | if (FALSE == found) 78 | return -1; 79 | return num; 80 | } 81 | 82 | int skip(char line[], int *index, int len, char ch) 83 | { 84 | while (*index < len && ch == line[*index]) 85 | (*index)++; 86 | return 0; 87 | 88 | } 89 | 90 | int getCommand(char line[], int flags, int *index, int *num, int len) 91 | { 92 | int cmd = line[(*index)++]; 93 | 94 | 95 | *num = getNumber(line, index, len); 96 | return cmd; 97 | } 98 | 99 | int parseCommand(char line[], int flags, int *index, int *num, int len) 100 | { 101 | int cmd = getCommand(line, flags, index, num, len); 102 | 103 | 104 | if (0 < cmd && 0 > *num) 105 | *num = 0; 106 | return toupper(cmd) | flags; 107 | } 108 | 109 | int parseRegister(char line[], 110 | int flags, 111 | int *index, 112 | int *num, int len, int *data_start, int *data_end, int complex_parse) 113 | { 114 | // need to handle S?, which queries that S register. 115 | int cmd = 0; 116 | 117 | 118 | cmd = getCommand(line, flags, index, num, len); 119 | if (0 > num) 120 | return AT_CMD_ERR; 121 | skip(line, index, len, ' '); 122 | if (len == *index) 123 | return AT_CMD_ERR; 124 | switch (line[(*index)++]) { 125 | case '=': 126 | // set a register 127 | skip(line, index, len, ' '); 128 | if (0 > getData(line, index, len, data_start, data_end, complex_parse)) 129 | return AT_CMD_ERR; 130 | break; 131 | case '?': 132 | // query a register 133 | flags |= AT_CMD_FLAG_QUERY; 134 | if (*num < 0) 135 | *num = 0; 136 | break; 137 | default: 138 | return AT_CMD_ERR; 139 | } 140 | 141 | return toupper(cmd) | flags; 142 | } 143 | 144 | int getcmd(char line[], int *index, int *num, int *data_start, int *data_end) 145 | { 146 | int len = 0; 147 | int cmd = AT_CMD_END; 148 | 149 | 150 | *num = 0; 151 | *data_start = 0; 152 | *data_end = 0; 153 | 154 | if (line == NULL) 155 | return AT_CMD_NONE; 156 | len = strlen(line); 157 | while (*index < len) { 158 | cmd = toupper(line[*index]); 159 | switch (cmd) { 160 | case ' ': 161 | break; 162 | case 0: 163 | return AT_CMD_END; 164 | case '%': 165 | (*index)++; 166 | while (*index < len) { 167 | switch (toupper(line[*index])) { 168 | case ' ': 169 | break; 170 | case 0: 171 | return AT_CMD_ERR; 172 | default: 173 | return parseCommand(line, AT_CMD_FLAG_PRO_PCT, index, num, len); 174 | } 175 | (*index)++; 176 | } 177 | break; 178 | case '\\': 179 | (*index)++; 180 | while (*index < len) { 181 | switch (toupper(line[*index])) { 182 | case ' ': 183 | break; 184 | case 0: 185 | return AT_CMD_ERR; 186 | default: 187 | return parseCommand(line, AT_CMD_FLAG_PRO_BACK, index, num, len); 188 | } 189 | (*index)++; 190 | } 191 | break; 192 | case ':': 193 | (*index)++; 194 | while (*index < len) { 195 | switch (toupper(line[*index])) { 196 | case ' ': 197 | break; 198 | case 0: 199 | return AT_CMD_ERR; 200 | default: 201 | return parseCommand(line, AT_CMD_FLAG_PRO_COLON, index, num, len); 202 | } 203 | (*index)++; 204 | } 205 | break; 206 | case '-': 207 | (*index)++; 208 | while (*index < len) { 209 | switch (toupper(line[*index])) { 210 | case ' ': 211 | break; 212 | case 0: 213 | return AT_CMD_ERR; 214 | default: 215 | return parseCommand(line, AT_CMD_FLAG_PRO_MINUS, index, num, len); 216 | } 217 | (*index)++; 218 | } 219 | break; 220 | case '&': 221 | (*index)++; 222 | while (*index < len) { 223 | switch (toupper(line[*index])) { 224 | case ' ': 225 | break; 226 | case 0: 227 | return AT_CMD_ERR; 228 | case 'Z': 229 | return parseRegister(line, AT_CMD_FLAG_EXT, index, num, len, data_start, data_end, 230 | TRUE); 231 | default: 232 | return parseCommand(line, AT_CMD_FLAG_EXT, index, num, len); 233 | } 234 | (*index)++; 235 | } 236 | break; 237 | case 'D': // handle Dialing. 238 | (*index)++; 239 | *num = 0; 240 | while (*index < len) { 241 | switch (toupper(line[*index])) { 242 | case 0: 243 | return cmd; 244 | case 'T': 245 | case 'P': 246 | case 'L': 247 | *num = toupper(line[*index]); 248 | (*index)++; 249 | default: 250 | getData(line, index, len, data_start, data_end, TRUE); 251 | return cmd; 252 | } 253 | (*index)++; 254 | } 255 | return cmd; 256 | case 'S': 257 | return parseRegister(line, AT_CMD_FLAG_BAS, index, num, len, data_start, data_end, FALSE); 258 | default: 259 | return parseCommand(line, AT_CMD_FLAG_BAS, index, num, len); 260 | } 261 | (*index)++; 262 | } 263 | return cmd; 264 | 265 | } 266 | 267 | int main_getcmd(int argc, char **argv) 268 | { 269 | char data[] = "DT 555-1212"; 270 | int index = 0, num = 0, start = 0, end = 0; 271 | int cmd = 0; 272 | 273 | 274 | while (cmd != AT_CMD_END) { 275 | cmd = getcmd(data, &index, &num, &start, &end); 276 | printf("Cmd: %c Index: %d Num: %d Start: %d End: %d\n", cmd, index, num, start, end); 277 | } 278 | 279 | return 0; 280 | } 281 | -------------------------------------------------------------------------------- /src/getcmd.h: -------------------------------------------------------------------------------- 1 | #define AT_CMD_END -1 2 | #define AT_CMD_ERR -2 3 | #define AT_CMD_NONE -4 4 | #define AT_CMD_IERR -3 5 | #define AT_CMD_FLAG_BAS 0 6 | #define AT_CMD_FLAG_EXT 256 7 | #define AT_CMD_FLAG_PRO_PCT 512 8 | #define AT_CMD_FLAG_PRO_BACK 1024 9 | #define AT_CMD_FLAG_PRO_MINUS 2048 10 | #define AT_CMD_FLAG_PRO_DOLLAR 4096 11 | #define AT_CMD_FLAG_PRO_COLON 8192 12 | #define AT_CMD_FLAG_QUERY 32768 13 | 14 | #ifndef TRUE 15 | #define TRUE 1 16 | #define FALSE 0 17 | #endif 18 | 19 | int getData(char line[], int *index, int len, int *data_start, int *data_end, int simple_parse); 20 | int getNumber(char line[], int *index, int len); 21 | int skip(char line[], int *index, int len, char ch); 22 | int getCommand(char line[], int flags, int *index, int *num, int len); 23 | int parseCommand(char line[], int flags, int *index, int *num, int len); 24 | int parseRegister(char line[], 25 | int flags, 26 | int *index, 27 | int *num, int len, int *data_start, int *data_end, int simple_parse); 28 | int getcmd(char line[], int *index, int *num, int *data_start, int *data_end); 29 | -------------------------------------------------------------------------------- /src/init.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include // for exit,atoi 3 | #include 4 | #include "debug.h" 5 | #include "phone_book.h" 6 | #include "init.h" 7 | 8 | void print_help(char *name) 9 | { 10 | fprintf(stderr, "Usage: %s \n", name); 11 | fprintf(stderr, " -p port to listen on (defaults to 6400)\n"); 12 | fprintf(stderr, " -- or -- \n"); 13 | fprintf(stderr, " -p ip_address:port to specify which ip address and port to listen on.\n"); 14 | fprintf(stderr, " -t trace flags: (can be combined)\n"); 15 | fprintf(stderr, " 's' = modem input\n"); 16 | fprintf(stderr, " 'S' = modem output\n"); 17 | fprintf(stderr, " 'i' = IP input\n"); 18 | fprintf(stderr, " 'I' = IP output\n"); 19 | fprintf(stderr, " -l 0 (NONE), 1 (FATAL) - 7 (DEBUG_X) (defaults to 0)\n"); 20 | fprintf(stderr, " -L log file (defaults to stderr)\n"); 21 | fprintf(stderr, "\n"); 22 | fprintf(stderr, " The following can be repeated for each modem desired\n"); 23 | fprintf(stderr, " (-s, -S, and -i will apply to any subsequent device if not set again)\n"); 24 | fprintf(stderr, "\n"); 25 | fprintf(stderr, " -d serial device (e.g. /dev/ttyS0). Cannot be used with -v\n"); 26 | fprintf(stderr, " -v tcp port for VICE RS232 (e.g. 25232). Cannot be used with -d\n"); 27 | fprintf(stderr, " -s serial port speed (defaults to 38400)\n"); 28 | fprintf(stderr, " -S speed modem will report (defaults to -s value)\n"); 29 | fprintf(stderr, " -I invert DCD pin\n"); 30 | fprintf(stderr, " -n add phone entry (number=replacement)\n"); 31 | fprintf(stderr, " -a filename to send to local side upon answer\n"); 32 | fprintf(stderr, " -A filename to send to remote side upon answer\n"); 33 | fprintf(stderr, " -c filename to send to local side upon connect\n"); 34 | fprintf(stderr, " -C filename to send to remote side upon connect\n"); 35 | fprintf(stderr, " -N filename to send when no answer\n"); 36 | fprintf(stderr, " -B filename to send when modem(s) busy\n"); 37 | fprintf(stderr, " -T filename to send upon inactivity timeout\n"); 38 | fprintf(stderr, 39 | " -i modem init string (defaults to '', leave off 'at' prefix when specifying)\n"); 40 | fprintf(stderr, 41 | " -D direct connection (follow with hostname:port for caller, : for receiver)\n"); 42 | exit(1); 43 | } 44 | 45 | int init(int argc, char **argv, modem_config cfg[], int max_modem, char **ip_addr, 46 | int *port, char *all_busy, int all_busy_len) 47 | { 48 | int i = 0; 49 | int j = 0; 50 | int opt = 0; 51 | int trace_flags = 0; 52 | char *tok; 53 | int dce_set = FALSE; 54 | int tty_set = FALSE; 55 | 56 | 57 | LOG_ENTER(); 58 | *port = 6400; 59 | mdm_init_config(&cfg[0]); 60 | cfg[0].dte_speed = 38400; 61 | cfg[0].dce_speed = 38400; 62 | 63 | while (opt > -1 && i < max_modem) { 64 | opt = getopt(argc, argv, "p:s:S:d:v:hw:i:Il:L:t:n:a:A:c:C:N:B:T:D:"); 65 | switch (opt) { 66 | case 't': 67 | trace_flags = log_get_trace_flags(); 68 | for (j = 0; j < strlen(optarg); j++) { 69 | switch (optarg[j]) { 70 | case 's': 71 | trace_flags |= TRACE_MODEM_IN; 72 | break; 73 | case 'S': 74 | trace_flags |= TRACE_MODEM_OUT; 75 | break; 76 | case 'i': 77 | trace_flags |= TRACE_IP_IN; 78 | break; 79 | case 'I': 80 | trace_flags |= TRACE_IP_OUT; 81 | break; 82 | } 83 | log_set_trace_flags(trace_flags); 84 | } 85 | break; 86 | case 'a': 87 | strncpy(cfg[i].data.local_answer, optarg, sizeof(cfg[i].data.local_answer)); 88 | break; 89 | case 'A': 90 | strncpy(cfg[i].data.remote_answer, optarg, sizeof(cfg[i].data.remote_answer)); 91 | break; 92 | case 'c': 93 | strncpy(cfg[i].data.local_connect, optarg, sizeof(cfg[i].data.local_connect)); 94 | break; 95 | case 'C': 96 | strncpy(cfg[i].data.remote_connect, optarg, sizeof(cfg[i].data.remote_connect)); 97 | break; 98 | case 'B': 99 | strncpy(all_busy, optarg, all_busy_len); 100 | break; 101 | case 'N': 102 | strncpy(cfg[i].data.no_answer, optarg, sizeof(cfg[i].data.no_answer)); 103 | break; 104 | case 'T': 105 | strncpy(cfg[i].data.inactive, optarg, sizeof(cfg[i].data.inactive)); 106 | break; 107 | case 'i': 108 | strncpy(cfg[i].config0, optarg, 255); 109 | break; 110 | case 'I': 111 | cfg[i].invert_dcd = TRUE; 112 | break; 113 | case 'p': 114 | if (strstr(optarg, ":") > 0) { 115 | *ip_addr = strtok(optarg, ":"); 116 | *port = (atoi(strtok(NULL,":"))); 117 | } 118 | else 119 | *port = (atoi(optarg)); 120 | break; 121 | case 'n': 122 | tok = strtok(optarg, "="); 123 | pb_add(tok, strtok(NULL, "=")); 124 | break; 125 | case 'l': 126 | log_set_level(atoi(optarg)); 127 | break; 128 | case 'L': 129 | log_set_file(fopen(optarg, "w+")); 130 | // should check to see if an error occurred... 131 | break; 132 | case 's': 133 | cfg[i].dte_speed = atoi(optarg); 134 | LOG(LOG_ALL, "Setting DTE speed to %d", cfg[i].dte_speed); 135 | if (dce_set == FALSE) 136 | cfg[i].dce_speed = cfg[i].dte_speed; 137 | break; 138 | case '?': 139 | case 'h': 140 | print_help(argv[0]); 141 | break; 142 | case 'd': 143 | case 'v': 144 | if (tty_set) { 145 | if (++i < max_modem) { 146 | dce_set = FALSE; 147 | mdm_init_config(&cfg[i]); 148 | cfg[i].dte_speed = cfg[i - 1].dte_speed; 149 | cfg[i].dce_speed = cfg[i - 1].dce_speed; 150 | cfg[i].dce_data.is_ip232 = FALSE; 151 | strncpy(cfg[i].config0, cfg[i - 1].config0, sizeof(cfg[i].config0)); 152 | strncpy(cfg[i].data.local_connect, cfg[i - 1].data.local_connect, 153 | sizeof(cfg[i].data.local_connect)); 154 | strncpy(cfg[i].data.remote_connect, cfg[i - 1].data.remote_connect, 155 | sizeof(cfg[i].data.remote_connect)); 156 | strncpy(cfg[i].data.local_answer, cfg[i - 1].data.local_answer, 157 | sizeof(cfg[i].data.local_answer)); 158 | strncpy(cfg[i].data.remote_answer, cfg[i - 1].data.remote_answer, 159 | sizeof(cfg[i].data.remote_answer)); 160 | strncpy(cfg[i].data.no_answer, cfg[i - 1].data.no_answer, 161 | sizeof(cfg[i].data.no_answer)); 162 | strncpy(cfg[i].data.inactive, cfg[i - 1].data.inactive, sizeof(cfg[i].data.inactive)); 163 | } 164 | else { 165 | LOG(LOG_WARN, "Maximum modems defined - ignoring extra"); 166 | break; 167 | } 168 | } 169 | strncpy(cfg[i].dce_data.tty, optarg, sizeof(cfg[i].dce_data.tty)); 170 | cfg[i].dce_data.is_ip232 = ('v' == opt); 171 | tty_set = TRUE; 172 | break; 173 | case 'S': 174 | cfg[i].dce_speed = atoi(optarg); 175 | dce_set = TRUE; 176 | break; 177 | case 'D': 178 | cfg[i].data.direct_conn = TRUE; 179 | strncpy(cfg[i].data.direct_conn_num, optarg, sizeof(cfg[i].data.direct_conn_num)); 180 | break; 181 | } 182 | } 183 | 184 | if (tty_set) { 185 | if (i < max_modem) 186 | ++i; 187 | } 188 | else { 189 | // no modems defined 190 | LOG(LOG_FATAL, "No modems defined"); 191 | print_help(argv[0]); 192 | } 193 | 194 | LOG(LOG_DEBUG, "Read configuration for %i serial port(s)", i); 195 | 196 | LOG_EXIT(); 197 | return i; 198 | } 199 | -------------------------------------------------------------------------------- /src/init.h: -------------------------------------------------------------------------------- 1 | #ifndef TRUE 2 | #define TRUE 1 3 | #define FALSE 0 4 | #endif 5 | 6 | #include "modem_core.h" 7 | 8 | extern void print_help(char *name); 9 | extern int init(int argc, char **argv, modem_config cfg[], int max_modem, char **ip_addr, 10 | int *port, char *all_busy, int all_busy_len); 11 | -------------------------------------------------------------------------------- /src/ip.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include // for read... 7 | #include // for atoi... 8 | 9 | #include "debug.h" 10 | #include "ip.h" 11 | 12 | const int BACK_LOG = 5; 13 | 14 | int ip_init_server_conn(char *ip_addr, int port) 15 | { 16 | int sSocket = 0, on = 0, rc = 0; 17 | struct sockaddr_in serverName = { 0 }; 18 | 19 | 20 | LOG_ENTER(); 21 | 22 | LOG(LOG_DEBUG, "Creating server socket"); 23 | 24 | sSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); 25 | if (-1 == sSocket) { 26 | ELOG(LOG_FATAL, "Server socket could not be created"); 27 | } 28 | else { 29 | 30 | /* 31 | * turn off bind address checking, and allow 32 | * port numbers to be reused - otherwise 33 | * the TIME_WAIT phenomenon will prevent 34 | * binding to these addresses. 35 | */ 36 | 37 | on = 1; 38 | 39 | rc = setsockopt(sSocket, SOL_SOCKET, SO_REUSEADDR, (const char *) &on, sizeof(on)); 40 | if (-1 == rc) { 41 | ELOG(LOG_ERROR, "bind address checking could not be turned off"); 42 | } 43 | 44 | if (ip_addr != NULL) /* gwb */ 45 | serverName.sin_addr.s_addr = inet_addr(ip_addr); 46 | else 47 | serverName.sin_addr.s_addr = htonl(INADDR_ANY); 48 | serverName.sin_family = AF_INET; 49 | 50 | /* network-order */ 51 | serverName.sin_port = htons(port); 52 | if (ip_addr != NULL ) /* gwb */ 53 | LOG(LOG_DEBUG, "Using specified ip address %s", ip_addr); 54 | 55 | LOG(LOG_DEBUG, "Binding server socket to port %d", port); 56 | rc = bind(sSocket, (struct sockaddr *) &serverName, sizeof(serverName) 57 | ); 58 | if (-1 == rc) { 59 | ELOG(LOG_FATAL, "Server socket could not be bound to port"); 60 | sSocket = -1; 61 | } 62 | else { 63 | LOG(LOG_INFO, "Server socket bound to port"); 64 | 65 | rc = listen(sSocket, BACK_LOG); 66 | LOG(LOG_INFO, "Server socket listening for connections"); 67 | if (-1 == rc) { 68 | ELOG(LOG_FATAL, "Server socket could not listen on port"); 69 | sSocket = -1; 70 | } 71 | } 72 | } 73 | LOG_EXIT(); 74 | return sSocket; 75 | } 76 | 77 | int ip_connect(char addy[]) 78 | { 79 | struct sockaddr_in pin; 80 | struct in_addr cin_addr; 81 | struct hostent *hp; 82 | int sd = 0; 83 | int port = 23; 84 | char *address; 85 | char *tmp; 86 | 87 | 88 | LOG_ENTER(); 89 | 90 | address = (char *) strtok(addy, ":"); 91 | tmp = (char *) strtok((char *) 0, ":"); 92 | if (tmp != NULL && strlen(tmp) > 0) { 93 | port = atoi(tmp); 94 | } 95 | 96 | LOG(LOG_DEBUG, "Calling %s", addy); 97 | memset(&pin, 0, sizeof(pin)); 98 | 99 | /* go find out about the desired host machine */ 100 | if ((hp = gethostbyname(address)) == 0) { 101 | // well, not a DNS entry... Treat as IP... 102 | if (1 != inet_aton(address, &cin_addr)) { 103 | ELOG(LOG_ERROR, "Host %s was invalid", addy); 104 | return -1; 105 | } 106 | } 107 | else { 108 | cin_addr = *((struct in_addr *) (hp->h_addr)); 109 | } 110 | 111 | pin.sin_family = AF_INET; 112 | pin.sin_addr.s_addr = cin_addr.s_addr; 113 | pin.sin_port = htons(port); 114 | 115 | /* grab an Internet domain socket */ 116 | if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { 117 | ELOG(LOG_ERROR, "could not create client socket"); 118 | return -1; 119 | } 120 | 121 | /* connect to PORT on HOST */ 122 | if (connect(sd, (struct sockaddr *) &pin, sizeof(pin)) == -1) { 123 | ELOG(LOG_ERROR, "could not connect to address"); 124 | return -1; 125 | } 126 | LOG(LOG_INFO, "Connection to %s established", addy); 127 | LOG_EXIT(); 128 | return sd; 129 | } 130 | 131 | int ip_accept(int sSocket) 132 | { 133 | struct sockaddr_in clientName = { 0 }; 134 | socklen_t clientLength = sizeof(clientName); 135 | int cSocket = -1; 136 | 137 | 138 | LOG_ENTER(); 139 | 140 | (void) memset(&clientName, 0, sizeof(clientName)); 141 | 142 | cSocket = accept(sSocket, (struct sockaddr *) &clientName, &clientLength); 143 | if (-1 == cSocket) { 144 | ELOG(LOG_ERROR, "Could not accept incoming connection"); 145 | return -1; 146 | } 147 | 148 | if (-1 == getpeername(cSocket, (struct sockaddr *) &clientName, &clientLength)) { 149 | ELOG(LOG_WARN, "Could not obtain peer name"); 150 | } 151 | else { 152 | LOG(LOG_INFO, "Connection accepted from %s", inet_ntoa(clientName.sin_addr) 153 | ); 154 | } 155 | LOG_EXIT(); 156 | return cSocket; 157 | } 158 | 159 | int ip_disconnect(int fd) 160 | { 161 | if (fd > -1) 162 | close(fd); 163 | return 0; 164 | } 165 | 166 | int ip_write(int fd, unsigned char *data, int len) 167 | { 168 | log_trace(TRACE_IP_OUT, (char *) data, len); 169 | return write(fd, data, len); 170 | } 171 | 172 | int ip_read(int fd, char *data, int len) 173 | { 174 | int res; 175 | 176 | 177 | res = recv(fd, data, len, 0); 178 | log_trace(TRACE_IP_IN, data, res); 179 | return res; 180 | } 181 | -------------------------------------------------------------------------------- /src/ip.h: -------------------------------------------------------------------------------- 1 | #ifndef IP_H 2 | #define IP_H 3 | 4 | #ifndef TRUE 5 | #define TRUE 1 6 | #define FALSE 0 7 | #endif 8 | 9 | extern int ip_init(void); 10 | extern int ip_init_server_conn(char *ip_addr, int port); 11 | extern int ip_connect(char addy[]); 12 | extern int ip_accept(int sSocket); 13 | extern int ip_disconnect(int fd); 14 | extern int ip_write(int fd, unsigned char *data, int len); 15 | extern int ip_read(int fd, char *data, int len); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/ip232.c: -------------------------------------------------------------------------------- 1 | #include // for recv... 2 | #include // for exit... 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include "util.h" 12 | #include "debug.h" 13 | #include "modem_core.h" 14 | #include "ip.h" 15 | #include "ip232.h" 16 | 17 | void *ip232_thread(void *arg) 18 | { 19 | modem_config *cfg = (modem_config *) arg; 20 | int accept_pending = FALSE; 21 | int rc; 22 | char buf[256]; 23 | 24 | fd_set readfs; 25 | int max_fd = 0; 26 | int cSocket; 27 | 28 | 29 | LOG_ENTER(); 30 | for (;;) { 31 | FD_ZERO(&readfs); 32 | FD_SET(cfg->dce_data.dp[1][0], &readfs); 33 | max_fd = cfg->dce_data.dp[1][0]; 34 | if (accept_pending == FALSE) { 35 | FD_SET(cfg->dce_data.sSocket, &readfs); 36 | max_fd = MAX(max_fd, cfg->dce_data.sSocket); 37 | } 38 | LOG(LOG_ALL, "Waiting for incoming ip232 connections"); 39 | rc = select(max_fd + 1, &readfs, NULL, NULL, NULL); 40 | if (rc < 0) { 41 | 42 | // handle error 43 | } 44 | else { 45 | if (FD_ISSET(cfg->dce_data.dp[1][0], &readfs)) { // pipe 46 | read(cfg->dce_data.dp[1][0], buf, sizeof(buf) - 1); 47 | LOG(LOG_DEBUG, "ip232 thread notified"); 48 | accept_pending = FALSE; 49 | } 50 | if (FD_ISSET(cfg->dce_data.sSocket, &readfs)) { // ip connection 51 | if (cfg->dce_data.ip232_is_connected) { 52 | LOG(LOG_DEBUG, "Already have ip232 connection, rejecting new"); 53 | 54 | // already have a connection... accept and close 55 | cSocket = ip_accept(cfg->dce_data.sSocket); 56 | if (cSocket > -1) { 57 | close(cSocket); 58 | } 59 | } 60 | else { 61 | LOG(LOG_DEBUG, "Incoming ip232 connection"); 62 | writePipe(cfg->dce_data.dp[0][1], MSG_ACCEPT); 63 | accept_pending = TRUE; 64 | } 65 | } 66 | } 67 | } 68 | LOG_EXIT(); 69 | } 70 | 71 | int spawn_ip232_thread(modem_config *cfg) 72 | { 73 | int rc; 74 | pthread_t thread_id; 75 | 76 | 77 | rc = pthread_create(&thread_id, NULL, ip232_thread, (void *) cfg); 78 | LOG(LOG_ALL, "ip232 thread ID=%d", (int) thread_id); 79 | if (rc < 0) { 80 | ELOG(LOG_FATAL, "ip232 thread could not be started"); 81 | exit(-1); 82 | } 83 | return 0; 84 | } 85 | 86 | int ip232_init_conn(modem_config *cfg) 87 | { 88 | int rc = -1; 89 | int port; 90 | 91 | 92 | LOG_ENTER(); 93 | LOG(LOG_INFO, "Opening ip232 device"); 94 | port = atoi(cfg->dce_data.tty); 95 | rc = ip_init_server_conn(NULL, port); 96 | if (rc < 0) { 97 | ELOG(LOG_FATAL, "Could not initialize ip232 server socket"); 98 | exit(-1); 99 | } 100 | if (-1 == pipe(cfg->dce_data.dp[0])) { 101 | ELOG(LOG_FATAL, "ip232 thread incoming IPC pipe could not be created"); 102 | exit(-1); 103 | } 104 | if (-1 == pipe(cfg->dce_data.dp[1])) { 105 | ELOG(LOG_FATAL, "ip232 thread outgoing IPC pipe could not be created"); 106 | exit(-1); 107 | } 108 | cfg->dce_data.sSocket = rc; 109 | cfg->dce_data.ip232_is_connected = FALSE; 110 | cfg->dce_data.fd = cfg->dce_data.dp[0][0]; 111 | spawn_ip232_thread(cfg); 112 | LOG(LOG_INFO, "ip232 device configured"); 113 | LOG_EXIT(); 114 | return rc; 115 | } 116 | 117 | int ip232_set_flow_control(modem_config *cfg, int status) 118 | { 119 | return 0; 120 | } 121 | 122 | int ip232_get_control_lines(modem_config *cfg) 123 | { 124 | int status = 0; 125 | 126 | 127 | if (cfg->dce_data.ip232_is_connected && cfg->dce_data.ip232_dtr) { 128 | status |= TIOCM_DSR; 129 | } 130 | return status; 131 | } 132 | 133 | int ip232_set_control_lines(modem_config *cfg, int state) 134 | { 135 | int dcd; 136 | unsigned char cmd[2]; 137 | 138 | 139 | if (cfg->dce_data.ip232_is_connected) { 140 | dcd = (state & TIOCM_DTR) ? TRUE : FALSE; 141 | if (dcd != cfg->dce_data.ip232_dcd) { 142 | cfg->dce_data.ip232_dcd = dcd; 143 | cmd[0] = 255; 144 | cmd[1] = dcd ? 1 : 0; 145 | ip_write(cfg->dce_data.fd, cmd, sizeof(cmd)); 146 | } 147 | } 148 | return 0; 149 | } 150 | 151 | int ip232_write(modem_config *cfg, unsigned char *data, int len) 152 | { 153 | int retval; 154 | int i = 0; 155 | int double_iac = FALSE; 156 | unsigned char text[1024]; 157 | int text_len = 0; 158 | 159 | 160 | log_trace(TRACE_MODEM_OUT, (char *) data, len); 161 | retval = len; 162 | if (cfg->dce_data.ip232_is_connected) { 163 | while (i < len) { 164 | if (double_iac) { 165 | text[text_len++] = 255; 166 | double_iac = FALSE; 167 | i++; 168 | } 169 | else { 170 | if (255 == data[i]) { 171 | text[text_len++] = 255; 172 | double_iac = TRUE; 173 | } 174 | else { 175 | text[text_len++] = data[i++]; 176 | } 177 | } 178 | if (text_len == sizeof(text)) { 179 | retval = ip_write(cfg->dce_data.fd, text, text_len); 180 | text_len = 0; 181 | } 182 | } 183 | if (text_len) { 184 | retval = ip_write(cfg->dce_data.fd, text, text_len); 185 | } 186 | } 187 | return retval; 188 | } 189 | 190 | int ip232_read(modem_config *cfg, unsigned char *data, int len) 191 | { 192 | int res; 193 | int rc; 194 | char buf[256]; 195 | int i = 0; 196 | int ch; 197 | int text_len = 0; 198 | 199 | 200 | LOG_ENTER(); 201 | if (len > sizeof(buf)) { 202 | LOG(LOG_FATAL, "ip232_read: len > sizeof(buf)"); 203 | exit(-1); 204 | } 205 | if (cfg->dce_data.ip232_is_connected) { 206 | res = recv(cfg->dce_data.fd, buf, len, 0); 207 | if (0 >= res) { 208 | LOG(LOG_INFO, "No ip232 socket data read, assume closed peer"); 209 | ip_disconnect(cfg->dce_data.fd); 210 | cfg->dce_data.fd = cfg->dce_data.dp[0][0]; 211 | cfg->dce_data.ip232_is_connected = FALSE; 212 | } 213 | else { 214 | LOG(LOG_DEBUG, "Read %d bytes from ip232 socket", res); 215 | log_trace(TRACE_MODEM_IN, buf, res); 216 | while (i < res) { 217 | ch = buf[i]; 218 | if (cfg->dce_data.ip232_iac) { 219 | cfg->dce_data.ip232_iac = FALSE; 220 | switch (ch) { 221 | case 0: 222 | cfg->dce_data.ip232_dtr = FALSE; 223 | break; 224 | case 1: 225 | cfg->dce_data.ip232_dtr = TRUE; 226 | break; 227 | case 255: 228 | data[text_len++] = 255; 229 | break; 230 | } 231 | } 232 | else { 233 | if (255 == ch) { 234 | cfg->dce_data.ip232_iac = TRUE; 235 | } 236 | else { 237 | data[text_len++] = ch; 238 | } 239 | } 240 | i++; 241 | } 242 | } 243 | } 244 | else { 245 | // not connected 246 | res = read(cfg->dce_data.dp[0][0], buf, sizeof(buf)); 247 | switch (buf[0]) { 248 | case MSG_ACCEPT: // accept connection. 249 | LOG(LOG_INFO, "Accepting ip232 connection..."); 250 | rc = ip_accept(cfg->dce_data.sSocket); 251 | if (res > -1) { 252 | cfg->dce_data.fd = rc; 253 | cfg->dce_data.ip232_is_connected = TRUE; 254 | cfg->dce_data.ip232_dtr = FALSE; 255 | cfg->dce_data.ip232_dcd = FALSE; 256 | writePipe(cfg->dce_data.dp[1][1], MSG_ACCEPTED); 257 | } 258 | break; 259 | } 260 | } 261 | 262 | LOG_EXIT(); 263 | return text_len; 264 | } 265 | -------------------------------------------------------------------------------- /src/ip232.h: -------------------------------------------------------------------------------- 1 | #ifndef IP232_H 2 | #define IP232_H 1 3 | 4 | #ifndef TRUE 5 | #define TRUE 1 6 | #define FALSE 0 7 | #endif /* */ 8 | 9 | #define MSG_ACCEPT '+' 10 | #define MSG_ACCEPTED '+' 11 | int ip232_init_conn(modem_config *); 12 | int ip232_set_flow_control(modem_config *, int status); 13 | int ip232_get_control_lines(modem_config *); 14 | int ip232_set_control_lines(modem_config *, int state); 15 | int ip232_write(modem_config *, unsigned char *data, int len); 16 | int ip232_read(modem_config *, unsigned char *data, int len); 17 | 18 | #endif /* */ 19 | -------------------------------------------------------------------------------- /src/line.c: -------------------------------------------------------------------------------- 1 | #include "debug.h" 2 | #include "modem_core.h" 3 | #include "phone_book.h" 4 | #include "ip.h" 5 | #include "bridge.h" 6 | #include "line.h" 7 | 8 | int line_init_config(modem_config *cfg) 9 | { 10 | cfg->line_data.fd = -1; 11 | cfg->line_data.is_telnet = FALSE; 12 | cfg->line_data.first_char = TRUE; 13 | cfg->line_data.valid_conn = FALSE; 14 | nvt_init_config(&cfg->line_data.nvt_data); 15 | return 0; 16 | } 17 | 18 | int line_write(modem_config *cfg, char *data, int len) 19 | { 20 | int retval; 21 | int i = 0; 22 | int double_iac = FALSE; 23 | unsigned char text[1024]; 24 | int text_len = 0; 25 | int mask = 0x7f; 26 | int ch; 27 | 28 | 29 | if (cfg->line_data.is_telnet || cfg->parity) { 30 | if (cfg->line_data.nvt_data.binary_xmit) 31 | mask = 0xff; 32 | 33 | retval = 0; 34 | while (i < len) { 35 | if (double_iac) { 36 | text[text_len++] = NVT_IAC; 37 | double_iac = FALSE; 38 | i++; 39 | } 40 | else { 41 | ch = data[i] & mask; 42 | if (NVT_IAC == ch) { 43 | text[text_len++] = NVT_IAC; 44 | double_iac = TRUE; 45 | } 46 | else { 47 | text[text_len++] = ch; 48 | i++; 49 | } 50 | } 51 | if (text_len == sizeof(text)) { 52 | retval = ip_write(cfg->line_data.fd, text, text_len); 53 | text_len = 0; 54 | } 55 | } 56 | if (text_len) { 57 | retval = ip_write(cfg->line_data.fd, text, text_len); 58 | } 59 | return retval; 60 | } 61 | 62 | return ip_write(cfg->line_data.fd, (unsigned char *) data, len); 63 | } 64 | 65 | int line_listen(modem_config *cfg) 66 | { 67 | return 0; 68 | } 69 | 70 | int line_off_hook(modem_config *cfg) 71 | { 72 | return 0; 73 | } 74 | 75 | int line_connect(modem_config *cfg) 76 | { 77 | char *addy = cfg->dialno; 78 | 79 | 80 | /* Reset everything we know about the line, it may not be the same as last time. */ 81 | line_init_config(cfg); 82 | 83 | LOG(LOG_INFO, "Connecting"); 84 | addy = pb_search(addy); 85 | cfg->line_data.fd = ip_connect(addy); 86 | if (cfg->line_data.fd > -1) { 87 | LOG(LOG_ALL, "Connected to %s", addy); 88 | cfg->line_data.valid_conn = TRUE; 89 | /* we need to let the other end know that our end will 90 | * handle the echo - otherwise "true" telnet clients like 91 | * those that come with Linux & Windows will echo characters 92 | * typed and you'll end up with doubled characters if the remote 93 | * host is echoing as well... 94 | * - gwb 95 | */ 96 | send_nvt_command(cfg->line_data.fd, &cfg->line_data.nvt_data, NVT_WILL, NVT_OPT_ECHO); 97 | 98 | /* If space parity is detected treat it as 8 bit and try to enable binary mode */ 99 | if (!cfg->parity) { 100 | send_nvt_command(cfg->line_data.fd, &cfg->line_data.nvt_data, 101 | NVT_WILL, NVT_OPT_TRANSMIT_BINARY); 102 | send_nvt_command(cfg->line_data.fd, &cfg->line_data.nvt_data, 103 | NVT_DO, NVT_OPT_TRANSMIT_BINARY); 104 | } 105 | return 0; 106 | } 107 | else { 108 | LOG(LOG_ALL, "Could not connect to %s", addy); 109 | cfg->line_data.valid_conn = FALSE; 110 | return -1; 111 | } 112 | } 113 | 114 | int line_disconnect(modem_config *cfg) 115 | { 116 | LOG(LOG_INFO, "Disconnecting"); 117 | if (cfg->data.direct_conn == TRUE) { 118 | LOG(LOG_INFO, "Direct connection active, maintaining link"); 119 | return -1; 120 | } 121 | else { 122 | cfg->line_data.is_telnet = FALSE; 123 | cfg->line_data.first_char = TRUE; 124 | if (cfg->line_data.valid_conn == TRUE) { 125 | ip_disconnect(cfg->line_data.fd); 126 | cfg->line_data.valid_conn = FALSE; 127 | } 128 | } 129 | return 0; 130 | } 131 | -------------------------------------------------------------------------------- /src/line.h: -------------------------------------------------------------------------------- 1 | #ifndef LINE_H 2 | #define LINE_H 1 3 | 4 | int line_init(void); 5 | int line_init_conn(modem_config *cfg); 6 | int line_init_config(modem_config *cfg); 7 | int line_read(modem_config *cfg, char *data, int len); 8 | int line_write(modem_config *cfg, char *data, int len); 9 | int line_listen(modem_config *cfg); 10 | int line_off_hook(modem_config *cfg); 11 | int line_connect(modem_config *cfg); 12 | int line_disconnect(modem_config *cfg); 13 | 14 | #endif 15 | -------------------------------------------------------------------------------- /src/modem_core.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include // for atoi 3 | 4 | #include "getcmd.h" 5 | #include "debug.h" 6 | #include "modem_core.h" 7 | 8 | #define gen_parity(v) (((((v) * 0x0101010101010101ULL) & 0x8040201008040201ULL) % 0x1FF) & 1) 9 | 10 | char *mdm_responses[37]; 11 | 12 | int mdm_init() 13 | { 14 | mdm_responses[MDM_RESP_OK] = "OK"; 15 | mdm_responses[MDM_RESP_RING] = "RING"; 16 | mdm_responses[MDM_RESP_ERROR] = "ERROR"; 17 | mdm_responses[MDM_RESP_CONNECT] = "CONNECT"; 18 | mdm_responses[MDM_RESP_NO_CARRIER] = "NO CARRIER"; 19 | mdm_responses[MDM_RESP_CONNECT_1200] = "CONNECT 1200"; 20 | mdm_responses[MDM_RESP_NO_DIALTONE] = "NO DIALTONE"; 21 | mdm_responses[MDM_RESP_BUSY] = "BUSY"; 22 | mdm_responses[MDM_RESP_NO_ANSWER] = "NO ANSWER"; 23 | mdm_responses[MDM_RESP_CONNECT_0600] = "CONNECT 0600"; 24 | mdm_responses[MDM_RESP_CONNECT_2400] = "CONNECT 2400"; 25 | mdm_responses[MDM_RESP_CONNECT_4800] = "CONNECT 4800"; 26 | mdm_responses[MDM_RESP_CONNECT_9600] = "CONNECT 9600"; 27 | mdm_responses[MDM_RESP_CONNECT_7200] = "CONNECT 7200"; 28 | mdm_responses[MDM_RESP_CONNECT_12000] = "CONNECT 12000"; 29 | mdm_responses[MDM_RESP_CONNECT_14400] = "CONNECT 14400"; 30 | mdm_responses[MDM_RESP_CONNECT_19200] = "CONNECT 19200"; 31 | mdm_responses[MDM_RESP_CONNECT_38400] = "CONNECT 38400"; 32 | mdm_responses[MDM_RESP_CONNECT_57600] = "CONNECT 57600"; 33 | mdm_responses[MDM_RESP_CONNECT_115200] = "CONNECT 115200"; 34 | mdm_responses[MDM_RESP_CONNECT_230400] = "CONNECT 230400"; 35 | mdm_responses[MDM_RESP_CONNECT_460800] = "CONNECT 460800"; 36 | mdm_responses[MDM_RESP_CONNECT_921600] = "CONNECT 921600"; 37 | return 0; 38 | } 39 | 40 | int get_connect_response(int speed, int level) 41 | { 42 | if (level == 0) { 43 | return MDM_RESP_CONNECT; 44 | } 45 | switch (speed) { 46 | case 921600: 47 | return MDM_RESP_CONNECT_921600; 48 | case 460800: 49 | return MDM_RESP_CONNECT_460800; 50 | case 230400: 51 | return MDM_RESP_CONNECT_230400; 52 | case 115200: 53 | return MDM_RESP_CONNECT_115200; 54 | case 57600: 55 | return MDM_RESP_CONNECT_57600; 56 | case 38400: 57 | return MDM_RESP_CONNECT_38400; 58 | case 19200: 59 | return MDM_RESP_CONNECT_19200; 60 | case 9600: 61 | return MDM_RESP_CONNECT_9600; 62 | case 4800: 63 | return MDM_RESP_CONNECT_4800; 64 | case 2400: 65 | return MDM_RESP_CONNECT_2400; 66 | case 1200: 67 | return MDM_RESP_CONNECT_1200; 68 | case 600: 69 | return MDM_RESP_CONNECT_0600; 70 | } 71 | return MDM_RESP_CONNECT; 72 | } 73 | 74 | void mdm_init_config(modem_config *cfg) 75 | { 76 | int i = 0; 77 | 78 | 79 | cfg->send_responses = TRUE; 80 | cfg->connect_response = 0; 81 | cfg->response_code_level = 4; 82 | cfg->text_responses = TRUE; 83 | cfg->echo = TRUE; 84 | cfg->cmd_mode = TRUE; 85 | cfg->conn_type = MDM_CONN_NONE; 86 | cfg->off_hook = FALSE; 87 | cfg->line_ringing = FALSE; 88 | cfg->cur_line_idx = 0; 89 | 90 | for (i = 0; i < 100; i++) { 91 | cfg->s[i] = 0; 92 | } 93 | cfg->s[SRegisterBreak] = 43; 94 | cfg->s[SRegisterCR] = 13; 95 | cfg->s[SRegisterLF] = 10; 96 | cfg->s[SRegisterBackspace] = 8; 97 | cfg->s[SRegisterBlindWait] = 2; 98 | cfg->s[SRegisterCarrierWait] = 50; 99 | cfg->s[SRegisterCommaPause] = 2; 100 | cfg->s[SRegisterCarrierTime] = 6; 101 | cfg->s[SRegisterCarrierLoss] = 14; 102 | cfg->s[SRegisterDTMFTime] = 95; 103 | cfg->s[SRegisterGuardTime] = 50; 104 | 105 | cfg->crlf[0] = cfg->s[SRegisterCR]; 106 | cfg->crlf[1] = cfg->s[SRegisterLF]; 107 | cfg->crlf[2] = 0; 108 | 109 | cfg->dial_type = 0; 110 | cfg->last_dial_type = 0; 111 | cfg->disconnect_delay = 0; 112 | 113 | cfg->pre_break_delay = FALSE; 114 | cfg->break_len = 0; 115 | 116 | cfg->memory_dial = FALSE; 117 | cfg->dsr_active = FALSE; 118 | cfg->dsr_on = TRUE; 119 | cfg->dcd_on = FALSE; 120 | cfg->found_a = FALSE; 121 | cfg->cmd_started = FALSE; 122 | cfg->allow_transmit = TRUE; 123 | cfg->invert_dsr = FALSE; 124 | cfg->invert_dcd = FALSE; 125 | 126 | cfg->config0[0] = '\0'; 127 | cfg->config1[0] = '\0'; 128 | 129 | dce_init_config(cfg); 130 | sh_init_config(cfg); 131 | } 132 | 133 | int get_new_cts_state(modem_config *cfg, int up) 134 | { 135 | return MDM_CL_CTS_HIGH; 136 | } 137 | 138 | int get_new_dsr_state(modem_config *cfg, int up) 139 | { 140 | if (cfg->dsr_on == TRUE) 141 | return (cfg->invert_dsr == TRUE ? MDM_CL_DSR_LOW : MDM_CL_DSR_HIGH); 142 | if ((up == TRUE && cfg->invert_dsr == FALSE) 143 | || (up == FALSE && cfg->invert_dsr == TRUE) 144 | ) 145 | return MDM_CL_DSR_HIGH; 146 | else 147 | return MDM_CL_DSR_LOW; 148 | } 149 | 150 | int get_new_dcd_state(modem_config *cfg, int up) 151 | { 152 | if (cfg->dcd_on == TRUE) 153 | return (cfg->invert_dcd == TRUE ? MDM_CL_DCD_LOW : MDM_CL_DCD_HIGH); 154 | if ((up == TRUE && cfg->invert_dcd == FALSE) 155 | || (up == FALSE && cfg->invert_dcd == TRUE) 156 | ) 157 | return MDM_CL_DCD_HIGH; 158 | else 159 | return MDM_CL_DCD_LOW; 160 | } 161 | 162 | int mdm_set_control_lines(modem_config *cfg) 163 | { 164 | int state = 0; 165 | int up = (cfg->conn_type == MDM_CONN_NONE ? FALSE : TRUE); 166 | 167 | 168 | state |= get_new_cts_state(cfg, up); 169 | state |= get_new_dsr_state(cfg, up); 170 | state |= get_new_dcd_state(cfg, up); 171 | 172 | LOG(LOG_INFO, "Control Lines: DSR:%d DCD:%d CTS:%d", 173 | ((state & MDM_CL_DSR_HIGH) != 0 ? 1 : 0), 174 | ((state & MDM_CL_DCD_HIGH) != 0 ? 1 : 0), ((state & MDM_CL_CTS_HIGH) != 0 ? 1 : 0) 175 | ); 176 | 177 | dce_set_control_lines(cfg, state); 178 | return 0; 179 | } 180 | 181 | /* Write single char bypassing parity generation since this mirrors input */ 182 | void mdm_write_char(modem_config *cfg, char data) 183 | { 184 | unsigned char str[2]; 185 | 186 | 187 | str[0] = data; 188 | dce_write(cfg, str, 1); 189 | } 190 | 191 | void mdm_write(modem_config *cfg, unsigned char *data, int len) 192 | { 193 | unsigned char *buf; 194 | int i; 195 | unsigned int v; 196 | 197 | 198 | if (cfg->allow_transmit == TRUE) { 199 | if (cfg->parity) { 200 | buf = malloc(len); 201 | memcpy(buf, data, len); 202 | 203 | for (i = 0; i < len; i++) { 204 | buf[i] &= 0x7f; 205 | v = buf[i]; 206 | v = gen_parity(v); 207 | buf[i] |= (((cfg->parity >> v)) & 1) << 7; 208 | } 209 | 210 | dce_write(cfg, buf, len); 211 | free(buf); 212 | } 213 | else 214 | dce_write(cfg, data, len); 215 | } 216 | } 217 | 218 | void mdm_send_response(int msg, modem_config *cfg) 219 | { 220 | char msgID[17]; 221 | 222 | 223 | LOG(LOG_DEBUG, "Sending %s response to modem", mdm_responses[msg]); 224 | if (cfg->send_responses == TRUE) { 225 | mdm_write(cfg, (unsigned char *) cfg->crlf, 2); 226 | if (cfg->text_responses == TRUE) { 227 | LOG(LOG_ALL, "Sending text response"); 228 | mdm_write(cfg, (unsigned char *) mdm_responses[msg], strlen(mdm_responses[msg])); 229 | } 230 | else { 231 | LOG(LOG_ALL, "Sending numeric response"); 232 | sprintf(msgID, "%d", msg); 233 | mdm_write(cfg, (unsigned char *) msgID, strlen(msgID)); 234 | } 235 | mdm_write(cfg, (unsigned char *) cfg->crlf, 2); 236 | } 237 | } 238 | 239 | int mdm_off_hook(modem_config *cfg) 240 | { 241 | LOG(LOG_INFO, "taking modem off hook"); 242 | cfg->off_hook = TRUE; 243 | cfg->cmd_mode = FALSE; 244 | line_off_hook(cfg); 245 | return 0; 246 | } 247 | 248 | int mdm_answer(modem_config *cfg) 249 | { 250 | if (cfg->line_ringing == TRUE) { 251 | cfg->conn_type = MDM_CONN_INCOMING; 252 | mdm_off_hook(cfg); 253 | mdm_set_control_lines(cfg); 254 | mdm_print_speed(cfg); 255 | } 256 | else if (cfg->conn_type == MDM_CONN_INCOMING) { 257 | // we are connected, just go off hook. 258 | mdm_off_hook(cfg); 259 | mdm_set_control_lines(cfg); 260 | } 261 | else { 262 | mdm_disconnect(cfg); 263 | } 264 | return 0; 265 | } 266 | 267 | int mdm_print_speed(modem_config *cfg) 268 | { 269 | int speed; 270 | 271 | 272 | switch (cfg->connect_response) { 273 | case 2: 274 | speed = cfg->dte_speed; 275 | break; 276 | default: 277 | speed = cfg->dce_speed; 278 | break; 279 | 280 | } 281 | mdm_send_response(get_connect_response(speed, cfg->response_code_level), cfg); 282 | return 0; 283 | } 284 | 285 | int mdm_connect(modem_config *cfg) 286 | { 287 | mdm_off_hook(cfg); 288 | if (cfg->conn_type == MDM_CONN_NONE) { 289 | if (line_connect(cfg) == 0) { 290 | cfg->conn_type = MDM_CONN_OUTGOING; 291 | mdm_set_control_lines(cfg); 292 | mdm_print_speed(cfg); 293 | } 294 | else { 295 | cfg->conn_type = MDM_CONN_OUTGOING; // so disconnect will print NO CARRIER 296 | mdm_disconnect(cfg); 297 | } 298 | } 299 | return 0; 300 | } 301 | 302 | int mdm_listen(modem_config *cfg) 303 | { 304 | return line_listen(cfg); 305 | } 306 | 307 | int mdm_disconnect(modem_config *cfg) 308 | { 309 | int type; 310 | 311 | 312 | LOG_ENTER(); 313 | LOG(LOG_INFO, "Disconnecting modem"); 314 | cfg->cmd_mode = TRUE; 315 | cfg->off_hook = FALSE; 316 | cfg->break_len = 0; 317 | cfg->line_ringing = FALSE; 318 | cfg->pre_break_delay = FALSE; 319 | if (0 == line_disconnect(cfg)) { 320 | type = cfg->conn_type; 321 | cfg->conn_type = MDM_CONN_NONE; 322 | mdm_set_control_lines(cfg); 323 | if (type != MDM_CONN_NONE) { 324 | mdm_send_response(MDM_RESP_NO_CARRIER, cfg); 325 | usleep(cfg->disconnect_delay * 1000); 326 | } 327 | cfg->rings = 0; 328 | mdm_listen(cfg); 329 | } 330 | else { 331 | // line still connected. 332 | } 333 | LOG_EXIT(); 334 | return 0; 335 | } 336 | 337 | int mdm_parse_cmd(modem_config *cfg) 338 | { 339 | int done = FALSE; 340 | int index = 0; 341 | int num = 0; 342 | int start = 0; 343 | int end = 0; 344 | int cmd = AT_CMD_NONE; 345 | char *command = cfg->cur_line; 346 | char tmp[256]; 347 | 348 | 349 | LOG_ENTER(); 350 | 351 | LOG(LOG_DEBUG, "Evaluating AT%s", command); 352 | 353 | while (TRUE != done) { 354 | if (cmd != AT_CMD_ERR) { 355 | cmd = getcmd(command, &index, &num, &start, &end); 356 | LOG(LOG_DEBUG, "Command: %c (%d), Flags: %d, index=%d, num=%d, data=%d-%d", 357 | (cmd > -1 ? cmd & 0xff : ' '), cmd, cmd >> 8, index, num, start, end); 358 | } 359 | switch (cmd) { 360 | case AT_CMD_ERR: 361 | mdm_send_response(MDM_RESP_ERROR, cfg); 362 | done = TRUE; 363 | break; 364 | case AT_CMD_END: 365 | if (cfg->cmd_mode == TRUE) 366 | mdm_send_response(MDM_RESP_OK, cfg); 367 | done = TRUE; 368 | break; 369 | case AT_CMD_NONE: 370 | done = TRUE; 371 | break; 372 | case 'O': 373 | case 'A': 374 | mdm_answer(cfg); 375 | cmd = AT_CMD_END; 376 | done = TRUE; 377 | break; 378 | case 'B': // 212A versus V.22 connection 379 | if (num > 1) { 380 | cmd = AT_CMD_ERR; 381 | } 382 | else { 383 | //cfg->connect_1200=num; 384 | } 385 | break; 386 | case 'D': 387 | if (end > start) { 388 | strncpy(cfg->dialno, command + start, end - start); 389 | cfg->dialno[end - start] = '\0'; 390 | cfg->dial_type = (char) num; 391 | cfg->last_dial_type = (char) num; 392 | strncpy(cfg->last_dialno, command + start, end - start); 393 | cfg->last_dialno[end - start] = '\0'; 394 | cfg->memory_dial = FALSE; 395 | } 396 | else if (num == 'L') { 397 | strncpy(cfg->dialno, cfg->last_dialno, strlen(cfg->last_dialno)); 398 | cfg->dial_type = cfg->dial_type; 399 | cfg->memory_dial = TRUE; 400 | mdm_write(cfg, (unsigned char *) cfg->crlf, 2); 401 | mdm_write(cfg, (unsigned char *) cfg->dialno, strlen(cfg->dialno)); 402 | } 403 | else { 404 | cfg->dialno[0] = 0; 405 | cfg->last_dialno[0] = 0; 406 | cfg->dial_type = 0; 407 | cfg->last_dial_type = 0; 408 | } 409 | if (strlen(cfg->dialno) > 0) { 410 | mdm_connect(cfg); 411 | } 412 | else { 413 | mdm_off_hook(cfg); 414 | } 415 | done = TRUE; 416 | break; 417 | case 'E': // still need to define #2 418 | if (num == 0) 419 | cfg->echo = FALSE; 420 | else if (num == 1) 421 | cfg->echo = TRUE; 422 | else { 423 | cmd = AT_CMD_ERR; 424 | } 425 | break; 426 | case 'H': 427 | if (num == 0) { 428 | mdm_disconnect(cfg); 429 | } 430 | else if (num == 1) { 431 | mdm_answer(cfg); 432 | } 433 | else 434 | cmd = AT_CMD_ERR; 435 | break; 436 | case 'I': // Information. 437 | break; 438 | case 'L': // Speaker volume 439 | if (num < 1 || num > 3) 440 | cmd = AT_CMD_ERR; 441 | else { 442 | //cfg->volume=num; 443 | } 444 | break; 445 | case 'M': // speaker settings 446 | if (num > 3) 447 | cmd = AT_CMD_ERR; 448 | else { 449 | //cfg->speaker_setting=num; 450 | } 451 | break; 452 | case 'N': // automode negotiate 453 | if (num > 1) 454 | cmd = AT_CMD_ERR; 455 | else { 456 | //cfg->auto_mode=num; 457 | } 458 | break; 459 | case 'P': // defaut to pulse dialing 460 | //cfg->default_dial_type=MDM_DT_PULSE; 461 | break; 462 | case 'Q': // still need to define #2 463 | if (num == 0) 464 | cfg->send_responses = TRUE; 465 | else if (num == 1) 466 | cfg->send_responses = FALSE; 467 | else if (num == 2) // this should be yes orig/no answer. 468 | cfg->send_responses = TRUE; 469 | else { 470 | cmd = AT_CMD_ERR; 471 | } 472 | break; 473 | case 'S': 474 | strncpy(tmp, command + start, end - start); 475 | tmp[end - start] = '\0'; 476 | cfg->s[num] = atoi(tmp); 477 | switch (num) { 478 | case 3: 479 | cfg->crlf[0] = cfg->s[SRegisterCR]; 480 | break; 481 | case 4: 482 | cfg->crlf[1] = cfg->s[SRegisterLF]; 483 | break; 484 | } 485 | break; 486 | case AT_CMD_FLAG_QUERY | 'S': 487 | sprintf(tmp, "%s%3.3d", cfg->crlf, cfg->s[num]); 488 | mdm_write(cfg, (unsigned char *) tmp, strlen(tmp)); 489 | break; 490 | case 'T': // defaut to tone dialing 491 | //cfg->default_dial_type=MDM_DT_TONE; 492 | break; 493 | case 'V': // done 494 | if (num == 0) 495 | cfg->text_responses = FALSE; 496 | else if (num == 1) 497 | cfg->text_responses = TRUE; 498 | else { 499 | cmd = AT_CMD_ERR; 500 | } 501 | break; 502 | case 'W': 503 | if (num > -1 && num < 3) 504 | cfg->connect_response = num; 505 | else 506 | cmd = AT_CMD_ERR; 507 | break; 508 | case 'X': 509 | if (num > -1 && num < 5) 510 | cfg->response_code_level = num; 511 | else 512 | cmd = AT_CMD_ERR; 513 | break; 514 | case 'Y': // long space disconnect. 515 | if (num > 1) 516 | cmd = AT_CMD_ERR; 517 | else { 518 | //cfg->long_disconnect=num; 519 | } 520 | break; 521 | case 'Z': // long space disconnect. 522 | if (num > 1) 523 | cmd = AT_CMD_ERR; 524 | else { 525 | // set config0 to cur_line and go. 526 | } 527 | break; 528 | case AT_CMD_FLAG_EXT + 'C': 529 | switch (num) { 530 | case 0: 531 | cfg->dcd_on = TRUE; 532 | mdm_set_control_lines(cfg); 533 | break; 534 | case 1: 535 | cfg->dcd_on = FALSE; 536 | mdm_set_control_lines(cfg); 537 | break; 538 | default: 539 | cmd = AT_CMD_ERR; 540 | break; 541 | } 542 | break; 543 | case AT_CMD_FLAG_EXT + 'K': 544 | // flow control. 545 | switch (num) { 546 | case 0: 547 | dce_set_flow_control(cfg, 0); 548 | break; 549 | case 3: 550 | dce_set_flow_control(cfg, MDM_FC_RTS); 551 | break; 552 | case 4: 553 | dce_set_flow_control(cfg, MDM_FC_XON); 554 | break; 555 | case 5: 556 | dce_set_flow_control(cfg, MDM_FC_XON); 557 | // need to add passthrough.. Not sure how. 558 | break; 559 | case 6: 560 | dce_set_flow_control(cfg, MDM_FC_XON | MDM_FC_RTS); 561 | break; 562 | default: 563 | cmd = AT_CMD_ERR; 564 | break; 565 | } 566 | break; 567 | default: 568 | break; 569 | } 570 | } 571 | cfg->cur_line_idx = 0; 572 | return cmd; 573 | } 574 | 575 | int detect_parity(modem_config *cfg) 576 | { 577 | int parity, eobits; 578 | int charA, charT; 579 | 580 | 581 | charA = cfg->pchars[0]; 582 | charT = cfg->pchars[1]; 583 | 584 | parity = (charA & 0x80) >> 7; 585 | parity |= (charT & 0x80) >> 6; 586 | 587 | eobits = gen_parity(charA & 0x7f); 588 | eobits |= gen_parity(charT & 0x7f) << 1; 589 | 590 | if (parity == eobits) 591 | return 2; 592 | 593 | if (parity && (parity ^ eobits) == (parity | eobits)) 594 | return 1; 595 | 596 | return parity & 0x3; 597 | } 598 | 599 | int mdm_handle_char(modem_config *cfg, char ch) 600 | { 601 | int parbit = ch & 0x80; 602 | 603 | 604 | ch &= 0x7f; // ignore parity 605 | 606 | if (cfg->echo == TRUE) 607 | mdm_write_char(cfg, (ch | parbit)); 608 | if (cfg->cmd_started == TRUE) { 609 | if (ch == (char) (cfg->s[SRegisterBackspace])) { 610 | if (cfg->cur_line_idx == 0 && cfg->echo == TRUE) { 611 | mdm_write(cfg, (unsigned char *) "T", 1); 612 | } 613 | else { 614 | cfg->cur_line_idx--; 615 | } 616 | } 617 | else if (ch == (char) (cfg->s[SRegisterCR])) { 618 | // we have a line, process. 619 | cfg->pchars[2] = ch | parbit; 620 | cfg->parity = detect_parity(cfg); 621 | cfg->cur_line[cfg->cur_line_idx] = 0; 622 | strncpy(cfg->last_cmd, cfg->cur_line, sizeof(cfg->last_cmd) - 1); 623 | mdm_parse_cmd(cfg); 624 | cfg->found_a = FALSE; 625 | cfg->cmd_started = FALSE; 626 | } 627 | else { 628 | cfg->cur_line[cfg->cur_line_idx++ % sizeof(cfg->cur_line)] = ch; 629 | } 630 | } 631 | else if (cfg->found_a == TRUE) { 632 | if (ch == 't' || ch == 'T') { 633 | cfg->cmd_started = TRUE; 634 | LOG(LOG_ALL, "'T' parsed in serial stream, switching to command parse mode"); 635 | cfg->pchars[1] = ch | parbit; 636 | } 637 | else if (ch == '/') { 638 | LOG(LOG_ALL, "'/' parsed in the serial stream, replaying last command"); 639 | cfg->cur_line_idx = strlen(cfg->last_cmd); 640 | strncpy(cfg->cur_line, cfg->last_cmd, cfg->cur_line_idx); 641 | mdm_parse_cmd(cfg); 642 | cfg->cmd_started = FALSE; 643 | } 644 | else if (ch != 'a' && ch != 'A') { 645 | cfg->found_a = FALSE; 646 | } 647 | 648 | } 649 | else if (ch == 'a' || ch == 'A') { 650 | LOG(LOG_ALL, "'A' parsed in serial stream"); 651 | cfg->found_a = TRUE; 652 | cfg->pchars[0] = ch | parbit; 653 | } 654 | return 0; 655 | } 656 | 657 | int mdm_clear_break(modem_config *cfg) 658 | { 659 | cfg->break_len = 0; 660 | cfg->pre_break_delay = FALSE; 661 | return 0; 662 | } 663 | 664 | int mdm_parse_data(modem_config *cfg, char *data, int len) 665 | { 666 | int i; 667 | int ch; 668 | 669 | 670 | if (cfg->cmd_mode == TRUE) { 671 | for (i = 0; i < len; i++) { 672 | mdm_handle_char(cfg, data[i]); 673 | } 674 | } 675 | else { 676 | line_write(cfg, data, len); 677 | if (cfg->pre_break_delay == TRUE) { 678 | for (i = 0; i < len; i++) { 679 | ch = data[i]; 680 | if (cfg->parity) 681 | ch &= 0x7f; 682 | if (ch == (char) cfg->s[SRegisterBreak]) { 683 | LOG(LOG_DEBUG, "Break character received"); 684 | cfg->break_len++; 685 | if (cfg->break_len > 3) { // more than 3, considered invalid 686 | cfg->pre_break_delay = FALSE; 687 | cfg->break_len = 0; 688 | } 689 | } 690 | else { 691 | LOG(LOG_ALL, "Found non-break character, cancelling break"); 692 | // chars past +++ 693 | mdm_clear_break(cfg); 694 | } 695 | } 696 | } 697 | } 698 | return 0; 699 | } 700 | 701 | int mdm_handle_timeout(modem_config *cfg) 702 | { 703 | if (cfg->pre_break_delay == TRUE && cfg->break_len == 3) { 704 | // pre and post break. 705 | LOG(LOG_INFO, "Break condition detected"); 706 | cfg->cmd_mode = TRUE; 707 | mdm_send_response(MDM_RESP_OK, cfg); 708 | mdm_clear_break(cfg); 709 | } 710 | else if (cfg->pre_break_delay == FALSE) { 711 | // pre break wait over. 712 | LOG(LOG_DEBUG, "Initial Break Delay detected"); 713 | cfg->pre_break_delay = TRUE; 714 | } 715 | else if (cfg->pre_break_delay == TRUE && cfg->break_len > 0) { 716 | LOG(LOG_ALL, "Inter-break-char delay time exceeded"); 717 | mdm_clear_break(cfg); 718 | } 719 | else if (cfg->s[30] != 0) { 720 | // timeout... 721 | LOG(LOG_INFO, "DTE communication inactivity timeout"); 722 | mdm_disconnect(cfg); 723 | } 724 | return 0; 725 | } 726 | 727 | int mdm_send_ring(modem_config *cfg) 728 | { 729 | LOG(LOG_DEBUG, "Sending 'RING' to modem"); 730 | cfg->line_ringing = TRUE; 731 | mdm_send_response(MDM_RESP_RING, cfg); 732 | cfg->rings++; 733 | LOG(LOG_ALL, "Sent #%d ring", cfg->rings); 734 | if (cfg->cmd_mode == FALSE || (cfg->s[0] != 0 && cfg->rings >= cfg->s[0])) { 735 | mdm_answer(cfg); 736 | } 737 | return 0; 738 | } 739 | -------------------------------------------------------------------------------- /src/modem_core.h: -------------------------------------------------------------------------------- 1 | #ifndef MODEM_CORE_H 2 | #define MODEM_CORE_H 1 3 | 4 | #define MDM_RESP_OK 0 5 | #define MDM_RESP_CONNECT 1 6 | #define MDM_RESP_RING 2 7 | #define MDM_RESP_NO_CARRIER 3 8 | #define MDM_RESP_ERROR 4 9 | #define MDM_RESP_CONNECT_1200 5 10 | #define MDM_RESP_NO_DIALTONE 6 11 | #define MDM_RESP_BUSY 7 12 | #define MDM_RESP_NO_ANSWER 8 13 | #define MDM_RESP_CONNECT_0600 9 14 | #define MDM_RESP_CONNECT_2400 10 15 | #define MDM_RESP_CONNECT_4800 11 16 | #define MDM_RESP_CONNECT_9600 12 17 | #define MDM_RESP_CONNECT_7200 13 18 | #define MDM_RESP_CONNECT_12000 14 19 | #define MDM_RESP_CONNECT_14400 15 20 | #define MDM_RESP_CONNECT_19200 16 21 | #define MDM_RESP_CONNECT_38400 17 22 | #define MDM_RESP_CONNECT_57600 18 23 | #define MDM_RESP_CONNECT_115200 19 24 | #define MDM_RESP_CONNECT_230400 20 25 | #define MDM_RESP_CONNECT_460800 21 26 | #define MDM_RESP_CONNECT_921600 22 27 | 28 | #define MDM_CL_DSR_LOW 0 29 | #define MDM_CL_DSR_HIGH 1 30 | #define MDM_CL_DCD_LOW 0 31 | #define MDM_CL_DCD_HIGH 2 32 | #define MDM_CL_CTS_LOW 0 33 | #define MDM_CL_CTS_HIGH 4 34 | #define MDM_CL_DTR_LOW 0 35 | #define MDM_CL_DTR_HIGH 8 36 | #define MDM_FC_RTS 1 37 | #define MDM_FC_XON 2 38 | 39 | #define MDM_CONN_NONE 0 40 | #define MDM_CONN_OUTGOING 1 41 | #define MDM_CONN_INCOMING 2 42 | 43 | #ifndef TRUE 44 | #define TRUE 1 45 | #define FALSE 0 46 | #endif 47 | 48 | #include "nvt.h" 49 | 50 | #ifndef MAX 51 | #define MAX(a, b) ((a) > (b) ? (a) : (b)) 52 | #endif 53 | 54 | typedef struct line_config { 55 | int valid_conn; 56 | int fd; 57 | int sfd; 58 | int is_telnet; 59 | int first_char; 60 | nvt_vars nvt_data; 61 | } line_config; 62 | 63 | typedef struct x_config { 64 | int mp[2][2]; 65 | int cp[2][2]; 66 | int wp[2][2]; 67 | char no_answer[256]; 68 | char local_connect[256]; 69 | char remote_connect[256]; 70 | char local_answer[256]; 71 | char remote_answer[256]; 72 | char inactive[256]; 73 | unsigned int direct_conn; 74 | char direct_conn_num[256]; 75 | } x_config; 76 | 77 | typedef struct dce_config { 78 | int is_ip232; 79 | char tty[256]; 80 | int first_char; 81 | int fd; 82 | int dp[2][2]; 83 | int sSocket; 84 | int ip232_is_connected; 85 | int ip232_dtr; 86 | int ip232_dcd; 87 | int ip232_iac; 88 | } dce_config; 89 | 90 | enum { 91 | SRegisterBreak = 2, 92 | SRegisterCR = 3, 93 | SRegisterLF = 4, 94 | SRegisterBackspace = 5, 95 | SRegisterBlindWait = 6, 96 | SRegisterCarrierWait = 7, 97 | SRegisterCommaPause = 8, 98 | SRegisterCarrierTime = 9, 99 | SRegisterCarrierLoss = 10, 100 | SRegisterDTMFTime = 11, 101 | SRegisterGuardTime = 12, 102 | }; 103 | 104 | typedef struct modem_config { 105 | // master configuration information 106 | 107 | // need to eventually change these 108 | dce_config dce_data; 109 | line_config line_data; 110 | x_config data; 111 | char config0[1024]; 112 | char config1[1024]; 113 | int dce_speed; 114 | int dte_speed; 115 | int conn_type; 116 | int line_ringing; 117 | int off_hook; 118 | int dsr_active; 119 | int dsr_on; 120 | int dcd_on; 121 | int invert_dsr; 122 | int invert_dcd; 123 | int allow_transmit; 124 | int rings; 125 | // command information 126 | int pre_break_delay; 127 | int found_a; 128 | int cmd_started; 129 | int cmd_mode; 130 | char last_cmd[1024]; 131 | char cur_line[1024]; 132 | int cur_line_idx; 133 | // dailing information 134 | char dialno[256]; 135 | char last_dialno[256]; 136 | char dial_type; 137 | char last_dial_type; 138 | int memory_dial; 139 | // modem config 140 | int connect_response; 141 | int response_code_level; 142 | int send_responses; 143 | int text_responses; 144 | int echo; 145 | int s[100]; 146 | int break_len; 147 | int disconnect_delay; 148 | char crlf[3]; 149 | int parity; 150 | char pchars[3]; 151 | } modem_config; 152 | 153 | int mdm_init(void); 154 | void mdm_init_config(modem_config *cfg); 155 | int get_new_cts_state(modem_config *cfg, int up); 156 | int get_new_dsr_state(modem_config *cfg, int up); 157 | int get_new_dcd_state(modem_config *cfg, int up); 158 | int mdm_set_control_lines(modem_config *cfg); 159 | void mdm_write(modem_config *cfg, unsigned char data[], int len); 160 | void mdm_send_response(int msg, modem_config *cfg); 161 | int mdm_off_hook(modem_config *cfg); 162 | int mdm_answer(modem_config *cfg); 163 | int mdm_print_speed(modem_config *cfg); 164 | int mdm_connect(modem_config *cfg); 165 | int mdm_listen(modem_config *cfg); 166 | int mdm_disconnect(modem_config *cfg); 167 | int mdm_parse_cmd(modem_config *cfg); 168 | int mdm_handle_char(modem_config *cfg, char ch); 169 | int mdm_clear_break(modem_config *cfg); 170 | int mdm_parse_data(modem_config *cfg, char *data, int len); 171 | int mdm_handle_timeout(modem_config *cfg); 172 | int mdm_send_ring(modem_config *cfg); 173 | 174 | #include "line.h" 175 | #include "shared.h" 176 | #include "dce.h" 177 | 178 | #endif 179 | -------------------------------------------------------------------------------- /src/nvt.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "debug.h" 4 | #include "ip.h" 5 | #include "nvt.h" 6 | 7 | void nvt_init_config(nvt_vars *vars) 8 | { 9 | int i; 10 | 11 | 12 | vars->binary_xmit = FALSE; 13 | vars->binary_recv = FALSE; 14 | for (i = 0; i < 256; i++) 15 | vars->term[i] = 0; 16 | 17 | return; 18 | } 19 | 20 | int get_nvt_cmd_response(int action, int type) 21 | { 22 | unsigned char rc = 0; 23 | 24 | 25 | if (type == TRUE) { 26 | switch (action) { 27 | case NVT_DO: 28 | rc = NVT_WILL_RESP; 29 | break; 30 | case NVT_DONT: 31 | rc = NVT_WONT_RESP; 32 | break; 33 | case NVT_WILL: 34 | rc = NVT_DO_RESP; 35 | break; 36 | case NVT_WONT: 37 | rc = NVT_DONT_RESP; 38 | break; 39 | } 40 | } 41 | else { 42 | switch (action) { 43 | case NVT_DO: 44 | case NVT_DONT: 45 | rc = NVT_WONT_RESP; 46 | break; 47 | case NVT_WILL: 48 | case NVT_WONT: 49 | rc = NVT_DONT_RESP; 50 | break; 51 | } 52 | } 53 | return rc; 54 | } 55 | 56 | int parse_nvt_subcommand(int fd, nvt_vars *vars, unsigned char *data, int len, int speed) 57 | { 58 | // overflow issue, again... 59 | nvtOption opt = data[2]; 60 | unsigned char resp[100]; 61 | unsigned char *response = resp + 3; 62 | int resp_len = 0; 63 | int response_len = 0; 64 | char tty_type[] = "VT100"; 65 | int rc; 66 | int slen = 0; 67 | char buf[50]; 68 | 69 | 70 | for (rc = 2; rc < len - 1; rc++) { 71 | if (NVT_IAC == data[rc]) 72 | if (NVT_SE == data[rc + 1]) { 73 | rc += 2; 74 | break; 75 | } 76 | } 77 | 78 | if (rc > 5 && (NVT_SB_SEND == data[3])) { 79 | switch (opt) { 80 | case NVT_OPT_TERMINAL_TYPE: 81 | case NVT_OPT_TERMINAL_SPEED: 82 | case NVT_OPT_X_DISPLAY_LOCATION: // should not have to have these 83 | case NVT_OPT_ENVIRON: // but telnet seems to expect. 84 | case NVT_OPT_NEW_ENVIRON: // them. 85 | response[response_len++] = NVT_SB_IS; 86 | switch (opt) { 87 | case NVT_OPT_TERMINAL_TYPE: 88 | slen = strlen(tty_type); 89 | strncpy((char *) response + response_len, tty_type, slen); 90 | response_len += slen; 91 | break; 92 | 93 | case NVT_OPT_TERMINAL_SPEED: 94 | sprintf(buf, "%i,%i", speed, speed); 95 | slen = strlen(buf); 96 | strncpy((char *) response + response_len, buf, slen); 97 | response_len += slen; 98 | break; 99 | 100 | default: 101 | break; 102 | } 103 | break; 104 | 105 | default: 106 | break; 107 | } 108 | } 109 | 110 | if (response_len) { 111 | resp[resp_len++] = NVT_IAC; 112 | resp[resp_len++] = NVT_SB; 113 | resp[resp_len++] = opt; 114 | resp_len += response_len; 115 | resp[resp_len++] = NVT_IAC; 116 | resp[resp_len++] = NVT_SE; 117 | ip_write(fd, resp, resp_len); 118 | } 119 | 120 | return rc; 121 | } 122 | 123 | void send_nvt_command(int fd, nvt_vars *vars, int action, int opt) 124 | { 125 | unsigned char cmd[3]; 126 | 127 | 128 | cmd[0] = NVT_IAC; 129 | cmd[1] = action; 130 | cmd[2] = opt; 131 | 132 | ip_write(fd, cmd, 3); 133 | vars->term[opt] = action; 134 | 135 | return; 136 | } 137 | 138 | void parse_nvt_command(int fd, nvt_vars *vars, nvtCommand action, nvtOption opt, int parity) 139 | { 140 | unsigned char resp[3]; 141 | int accept = FALSE; 142 | 143 | 144 | resp[0] = NVT_IAC; 145 | resp[2] = opt; 146 | 147 | switch (opt) { 148 | case NVT_OPT_TRANSMIT_BINARY: 149 | switch (action) { 150 | case NVT_DO: 151 | if (!parity) { 152 | if (!vars->binary_xmit) { 153 | LOG(LOG_INFO, "Enabling telnet binary xmit"); 154 | vars->binary_xmit = TRUE; 155 | accept = TRUE; 156 | } 157 | else 158 | return; 159 | } 160 | break; 161 | case NVT_DONT: 162 | if (vars->binary_xmit) { 163 | LOG(LOG_INFO, "Disabling telnet binary xmit"); 164 | vars->binary_xmit = FALSE; 165 | accept = TRUE; 166 | } 167 | else 168 | return; 169 | break; 170 | case NVT_WILL: 171 | if (!parity) { 172 | if (!vars->binary_recv) { 173 | LOG(LOG_INFO, "Enabling telnet binary recv"); 174 | vars->binary_recv = TRUE; 175 | accept = TRUE; 176 | } 177 | else 178 | return; 179 | } 180 | break; 181 | case NVT_WONT: 182 | if (vars->binary_recv) { 183 | LOG(LOG_INFO, "Disabling telnet binary recv"); 184 | vars->binary_recv = FALSE; 185 | accept = TRUE; 186 | } 187 | else 188 | return; 189 | break; 190 | 191 | default: 192 | break; 193 | } 194 | resp[1] = get_nvt_cmd_response(action, accept); 195 | break; 196 | 197 | case NVT_OPT_NAWS: 198 | case NVT_OPT_TERMINAL_TYPE: 199 | case NVT_OPT_TERMINAL_SPEED: 200 | case NVT_OPT_SUPPRESS_GO_AHEAD: 201 | case NVT_OPT_ECHO: 202 | case NVT_OPT_X_DISPLAY_LOCATION: // should not have to have these 203 | case NVT_OPT_ENVIRON: // but telnet seems to expect. 204 | case NVT_OPT_NEW_ENVIRON: // them. 205 | resp[1] = get_nvt_cmd_response(action, TRUE); 206 | break; 207 | 208 | default: 209 | resp[1] = get_nvt_cmd_response(action, FALSE); 210 | break; 211 | } 212 | ip_write(fd, resp, 3); 213 | return; 214 | } 215 | -------------------------------------------------------------------------------- /src/nvt.h: -------------------------------------------------------------------------------- 1 | #ifndef NVT_H 2 | #define NVT_H 1 3 | 4 | typedef enum { 5 | NVT_SE = 240, 6 | NVT_NOP = 241, 7 | NVT_DM = 242, 8 | NVT_SB = 250, 9 | NVT_WILL = 251, 10 | NVT_WONT = 252, 11 | NVT_DO = 253, 12 | NVT_DONT = 254, 13 | NVT_IAC = 255, 14 | NVT_WILL_RESP = 251, 15 | NVT_WONT_RESP = 252, 16 | NVT_DO_RESP = 253, 17 | NVT_DONT_RESP = 254, 18 | } nvtCommand; 19 | 20 | typedef enum { 21 | NVT_OPT_TRANSMIT_BINARY = 0, 22 | NVT_OPT_ECHO = 1, 23 | NVT_OPT_SUPPRESS_GO_AHEAD = 3, 24 | NVT_OPT_STATUS = 5, 25 | NVT_OPT_RCTE = 7, 26 | NVT_OPT_TIMING_MARK = 6, 27 | NVT_OPT_NAOCRD = 10, 28 | NVT_OPT_TERMINAL_TYPE = 24, 29 | NVT_OPT_NAWS = 31, 30 | NVT_OPT_TERMINAL_SPEED = 32, 31 | NVT_OPT_LINEMODE = 34, 32 | NVT_OPT_X_DISPLAY_LOCATION = 35, 33 | NVT_OPT_ENVIRON = 36, 34 | NVT_OPT_NEW_ENVIRON = 39, 35 | } nvtOption; 36 | 37 | #define NVT_SB_IS 0 38 | #define NVT_SB_SEND 1 39 | 40 | #ifndef TRUE 41 | #define TRUE 1 42 | #define FALSE 0 43 | #endif 44 | 45 | typedef struct nvt_vars { 46 | int binary_xmit; 47 | int binary_recv; 48 | char term[256]; 49 | } nvt_vars; 50 | 51 | extern void nvt_init_config(nvt_vars *vars); 52 | extern int get_nvt_cmd_response(int action, int type); 53 | extern int parse_nvt_subcommand(int fd, nvt_vars *vars, unsigned char *data, int len, int speed); 54 | extern void send_nvt_command(int fd, nvt_vars *vars, int action, int opt); 55 | extern void parse_nvt_command(int fd, nvt_vars *vars, nvtCommand action, nvtOption opt, int parity); 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /src/phone_book.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "phone_book.h" 4 | #include "debug.h" 5 | 6 | #define PBSIZE 100 7 | 8 | char phone_book[PBSIZE][2][128]; 9 | int size = 0; 10 | 11 | int pb_init() 12 | { 13 | return 0; 14 | } 15 | 16 | int pb_add(char *from, char *to) 17 | { 18 | LOG_ENTER(); 19 | if (size < PBSIZE && from != NULL && to != NULL && strlen(from) > 0 && strlen(to) > 0) { 20 | // should really trim spaces. 21 | strncpy(phone_book[size][0], from, sizeof(phone_book[size][0])); 22 | strncpy(phone_book[size][1], to, sizeof(phone_book[size][1])); 23 | size++; 24 | LOG_EXIT(); 25 | return 0; 26 | } 27 | LOG_EXIT(); 28 | return -1; 29 | } 30 | 31 | char *pb_search(char *number) 32 | { 33 | int i = 0; 34 | 35 | LOG_ENTER(); 36 | for (i = 0; i < size; i++) { 37 | if (strcmp(phone_book[i][0], number) == 0) { 38 | 39 | LOG(LOG_INFO, "Found a match for '%s': '%s'", number, phone_book[i][1]); 40 | strcpy(number, phone_book[i][1]); 41 | break; 42 | } 43 | } 44 | LOG_EXIT(); 45 | return number; 46 | } 47 | -------------------------------------------------------------------------------- /src/phone_book.h: -------------------------------------------------------------------------------- 1 | int pb_init(void); 2 | int pb_add(char *from, char *to); 3 | char *pb_search(char *number); 4 | -------------------------------------------------------------------------------- /src/serial.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "debug.h" 8 | #include "serial.h" 9 | 10 | int ser_get_bps_const(int speed) 11 | { 12 | int bps_rate = 0; 13 | 14 | LOG_ENTER(); 15 | 16 | LOG(LOG_DEBUG, "Checking speed: %d", speed); 17 | 18 | switch (speed) { 19 | #ifdef B921600 20 | case 921600: 21 | bps_rate = B921600; 22 | break; 23 | #endif 24 | #ifdef B460800 25 | case 460800: 26 | bps_rate = B460800; 27 | break; 28 | #endif 29 | case 230400: 30 | bps_rate = B230400; 31 | break; 32 | case 115200: 33 | bps_rate = B115200; 34 | break; 35 | case 57600: 36 | bps_rate = B57600; 37 | break; 38 | case 38400: 39 | bps_rate = B38400; 40 | break; 41 | case 19200: 42 | bps_rate = B19200; 43 | break; 44 | case 9600: 45 | bps_rate = B9600; 46 | break; 47 | case 4800: 48 | bps_rate = B4800; 49 | break; 50 | case 2400: 51 | bps_rate = B2400; 52 | break; 53 | case 1200: 54 | bps_rate = B1200; 55 | break; 56 | case 600: 57 | bps_rate = B600; 58 | break; 59 | case 300: 60 | bps_rate = B300; 61 | break; 62 | case 150: 63 | bps_rate = B150; 64 | break; 65 | case 134: 66 | bps_rate = B134; 67 | break; 68 | case 110: 69 | bps_rate = B110; 70 | break; 71 | case 75: 72 | bps_rate = B75; 73 | break; 74 | case 50: 75 | bps_rate = B50; 76 | break; 77 | case 0: 78 | bps_rate = B0; 79 | break; 80 | default: 81 | ELOG(LOG_FATAL, "Unknown baud rate"); 82 | bps_rate = -1; 83 | } 84 | LOG_EXIT(); 85 | return bps_rate; 86 | 87 | } 88 | 89 | int ser_init_conn(char *tty, int speed) 90 | { 91 | int fd = -1; 92 | struct termios tio; 93 | int bps_rate = 0; 94 | 95 | LOG_ENTER(); 96 | 97 | bps_rate = ser_get_bps_const(speed); 98 | 99 | if (bps_rate > -1) { 100 | /* open the device to be non-blocking (read will return immediatly) */ 101 | LOG(LOG_INFO, "Opening serial device"); 102 | 103 | fd = open(tty, O_RDWR | O_NOCTTY | O_NONBLOCK); 104 | 105 | if (fd < 0) { 106 | ELOG(LOG_FATAL, "TTY %s could not be opened", tty); 107 | } 108 | else { 109 | LOG(LOG_INFO, "Opened serial device %s at speed %d as fd %d", tty, speed, fd); 110 | 111 | /* Make the file descriptor asynchronous (the manual page says only 112 | O_APPEND and O_NONBLOCK, will work with F_SETFL...) */ 113 | fcntl(fd, F_SETFL, FASYNC); 114 | 115 | tio.c_cflag = CS8 | CLOCAL | CREAD | CRTSCTS; 116 | tio.c_iflag = IGNBRK; 117 | tio.c_oflag = 0; 118 | tio.c_lflag = 0; 119 | cfsetispeed(&tio, bps_rate); 120 | cfsetospeed(&tio, bps_rate); 121 | 122 | tio.c_cc[VMIN] = 1; 123 | tio.c_cc[VTIME] = 0; 124 | 125 | tcflush(fd, TCIFLUSH); 126 | tcsetattr(fd, TCSANOW, &tio); 127 | LOG(LOG_INFO, "serial device configured"); 128 | } 129 | } 130 | 131 | LOG_EXIT(); 132 | return fd; 133 | } 134 | 135 | int ser_set_flow_control(int fd, int status) 136 | { 137 | struct termios tio; 138 | 139 | if (0 != tcgetattr(fd, &tio)) { 140 | ELOG(LOG_FATAL, "Could not get serial port attributes"); 141 | return -1; 142 | } 143 | // turn all off. 144 | tio.c_cflag &= ~(IXON | IXOFF | CRTSCTS); 145 | tio.c_cflag |= status; 146 | if (0 != tcsetattr(fd, TCSANOW, &tio)) { 147 | ELOG(LOG_FATAL, "Could not set serial port attributes"); 148 | return -1; 149 | } 150 | return 0; 151 | } 152 | 153 | int ser_get_control_lines(int fd) 154 | { 155 | int status; 156 | 157 | 158 | if (0 > ioctl(fd, TIOCMGET, &status)) { 159 | ELOG(LOG_FATAL, "Could not obtain serial port status"); 160 | return -1; 161 | } 162 | return status; 163 | } 164 | 165 | int ser_set_control_lines(int fd, int state) 166 | { 167 | int status; 168 | 169 | 170 | if (0 > (status = ser_get_control_lines(fd))) { 171 | return status; 172 | } 173 | status &= ~(TIOCM_RTS | TIOCM_DTR); 174 | status |= state; 175 | if (0 > ioctl(fd, TIOCMSET, &status)) { 176 | #ifndef WIN32 177 | ELOG(LOG_FATAL, "Could not set serial port status"); 178 | return -1; 179 | #else 180 | ELOG(LOG_WARN, "Could not set serial port status, CYGWIN bug?"); 181 | #endif 182 | } 183 | return 0; 184 | } 185 | 186 | int ser_write(int fd, unsigned char *data, int len) 187 | { 188 | log_trace(TRACE_MODEM_OUT, (char *) data, len); 189 | return write(fd, data, len); 190 | } 191 | 192 | int ser_read(int fd, unsigned char *data, int len) 193 | { 194 | int res; 195 | 196 | 197 | res = read(fd, data, len); 198 | log_trace(TRACE_MODEM_IN, (char *) data, res); 199 | return res; 200 | } 201 | -------------------------------------------------------------------------------- /src/serial.h: -------------------------------------------------------------------------------- 1 | #ifndef SERIAL_H 2 | #define SERIAL_H 1 3 | 4 | #ifndef TRUE 5 | #define TRUE 1 6 | #define FALSE 0 7 | #endif 8 | 9 | int ser_get_bps_const(int speed); 10 | int ser_init_conn(char *tty, int speed); 11 | int ser_set_flow_control(int fd, int status); 12 | int ser_get_control_lines(int fd); 13 | int ser_set_control_lines(int fd, int state); 14 | int ser_write(int fd, unsigned char *data, int len); 15 | int ser_read(int fd, unsigned char *data, int len); 16 | 17 | #endif 18 | -------------------------------------------------------------------------------- /src/shared.c: -------------------------------------------------------------------------------- 1 | #include "modem_core.h" 2 | 3 | int sh_init_config(modem_config *cfg) 4 | { 5 | cfg->data.local_connect[0] = 0; 6 | cfg->data.remote_connect[0] = 0; 7 | cfg->data.local_answer[0] = 0; 8 | cfg->data.remote_answer[0] = 0; 9 | cfg->data.no_answer[0] = 0; 10 | cfg->data.inactive[0] = 0; 11 | cfg->data.direct_conn = FALSE; 12 | cfg->data.direct_conn_num[0] = 0; 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /src/shared.h: -------------------------------------------------------------------------------- 1 | #ifndef SHARED_H 2 | #define SHARED_H 1 3 | 4 | int sh_init_config(modem_config *cfg); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /src/tcpser.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | #include "init.h" 11 | #include "ip.h" 12 | #include "modem_core.h" 13 | #include "bridge.h" 14 | #include "phone_book.h" 15 | #include "util.h" 16 | #include "debug.h" 17 | 18 | const char MDM_BUSY[] = "BUSY\n"; 19 | 20 | int main(int argc, char *argv[]) 21 | { 22 | modem_config cfg[64]; 23 | int modem_count; 24 | int port = 0; 25 | 26 | char *ip_addr = NULL; /* gwb */ 27 | char all_busy[255]; 28 | 29 | pthread_t thread_id; 30 | int i; 31 | int rc; 32 | 33 | int sSocket = 0; 34 | fd_set readfs; 35 | int max_fd = 0; 36 | int accept_pending = FALSE; 37 | 38 | int res = 0; 39 | char buf[255]; 40 | 41 | int cSocket; 42 | 43 | log_init(); 44 | 45 | LOG_ENTER(); 46 | 47 | log_set_level(LOG_FATAL); 48 | 49 | mdm_init(); 50 | 51 | pb_init(); 52 | 53 | signal(SIGIO, SIG_IGN); /* Some Linux variant term on SIGIO by default */ 54 | 55 | modem_count = init(argc, argv, cfg, 64, &ip_addr, &port,all_busy,sizeof(all_busy)); /* gwb */ 56 | 57 | sSocket = ip_init_server_conn(ip_addr, port); 58 | 59 | for (i = 0; i < modem_count; i++) { 60 | if (-1 == pipe(cfg[i].data.mp[0])) { 61 | ELOG(LOG_FATAL, "Bridge task incoming IPC pipe could not be created"); 62 | exit(-1); 63 | } 64 | if (-1 == pipe(cfg[i].data.mp[1])) { 65 | ELOG(LOG_FATAL, "Bridge task outgoing IPC pipe could not be created"); 66 | exit(-1); 67 | } 68 | if (dce_init_conn(&cfg[i]) < 0) { 69 | LOG(LOG_FATAL, "Could not open serial port %s", cfg->dce_data.tty); 70 | exit(-1); 71 | } 72 | cfg[i].line_data.sfd = sSocket; 73 | 74 | rc = pthread_create(&thread_id, NULL, *run_bridge, (void *) &cfg[i]); 75 | if (rc < 0) { 76 | ELOG(LOG_FATAL, "IP thread could not be started"); 77 | exit(-1); 78 | } 79 | 80 | } 81 | for (;;) { 82 | FD_ZERO(&readfs); 83 | max_fd = 0; 84 | for (i = 0; i < modem_count; i++) { 85 | FD_SET(cfg[i].data.mp[0][0], &readfs); 86 | max_fd = MAX(max_fd, cfg[i].data.mp[0][0]); 87 | } 88 | if (accept_pending == FALSE) { 89 | max_fd = MAX(max_fd, sSocket); 90 | FD_SET(sSocket, &readfs); 91 | } 92 | LOG(LOG_ALL, "Waiting for incoming connections and/or indicators"); 93 | select(max_fd + 1, &readfs, NULL, NULL, NULL); 94 | for (i = 0; i < modem_count; i++) { 95 | if (FD_ISSET(cfg[i].data.mp[0][0], &readfs)) { // child pipe 96 | res = read(cfg[i].data.mp[0][0], buf, sizeof(buf) - 1); 97 | if (res > -1) { 98 | buf[res] = 0; 99 | LOG(LOG_DEBUG, "modem core #%d sent response '%c'", i, buf[0]); 100 | accept_pending = FALSE; 101 | } 102 | } 103 | } 104 | if (FD_ISSET(sSocket, &readfs)) { // IP traffic 105 | if (!accept_pending) { 106 | LOG(LOG_DEBUG, "Incoming connection pending"); 107 | // first try for a modem that is listening. 108 | for (i = 0; i < modem_count; i++) { 109 | if (cfg[i].s[0] != 0 && cfg[i].off_hook == FALSE) { 110 | // send signal to pipe saying pick up... 111 | LOG(LOG_DEBUG, "Sending incoming connection to listening modem #%d", i); 112 | writePipe(cfg[i].data.mp[1][1], MSG_ACCEPT); 113 | accept_pending = TRUE; 114 | break; 115 | } 116 | } 117 | // now, send to any non-active modem. 118 | for (i = 0; i < modem_count; i++) { 119 | if (cfg[i].off_hook == FALSE) { 120 | // send signal to pipe saying pick up... 121 | LOG(LOG_DEBUG, "Sending incoming connection to non-connected modem #%d", i); 122 | writePipe(cfg[i].data.mp[1][1], MSG_ACCEPT); 123 | accept_pending = TRUE; 124 | break; 125 | } 126 | } 127 | if (i == modem_count) { 128 | LOG(LOG_DEBUG, "No open modem to send to, send notice and close"); 129 | // no connections.., accept and print error 130 | cSocket = ip_accept(sSocket); 131 | if (cSocket > -1) { 132 | if (strlen(all_busy) < 1) { 133 | ip_write(cSocket, (unsigned char *) MDM_BUSY, strlen(MDM_BUSY)); 134 | } 135 | else { 136 | writeFile(all_busy, cSocket); 137 | } 138 | close(cSocket); 139 | } 140 | } 141 | } 142 | } 143 | } 144 | LOG_EXIT(); 145 | return rc; 146 | } 147 | -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "util.h" 5 | 6 | int writePipe(int fd, char msg) 7 | { 8 | char tmp[3]; 9 | 10 | tmp[0] = msg; 11 | tmp[1] = '\n'; 12 | tmp[2] = '\0'; 13 | 14 | //printf("Writing %c to pipe fd: %d\n",msg,fd); 15 | 16 | return write(fd, tmp, 2); 17 | } 18 | 19 | int writeFile(char *name, int fd) 20 | { 21 | FILE *file; 22 | char buf[255]; 23 | size_t len; 24 | size_t size = 1; 25 | size_t max = 255; 26 | 27 | if (NULL != (file = fopen(name, "rb"))) { 28 | while (0 < (len = fread(buf, size, max, file))) { 29 | write(fd, buf, len); 30 | } 31 | fclose(file); 32 | return 0; 33 | } 34 | return -1; 35 | } 36 | -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | int writePipe(int fd, char msg); 2 | int writeFile(char *name, int fd); 3 | --------------------------------------------------------------------------------