Added parity generation.

This commit is contained in:
Chris Osborn 2016-05-24 09:06:48 -07:00
commit 9c807bde6a
35 changed files with 3850 additions and 0 deletions

43
CHANGES Normal file
View File

@ -0,0 +1,43 @@
1.0
Initial release
1.0rc2
Cleaned up code
10.rc3
changed char fields to unsigned char
Changed many functions to return status
Added macros for __FUNCTION__ keyword, for compiler sanity
Added include headers needed for other UNIX platforms
1.0rc4
Changed cfg->connection to cfg->conn_type
Added support for send file text to connections on actions
If data received while off-hook and no conn, go on-hook.
Clean up parseRegister action in getcmd.c
Add support for ':' and '-' extended commands
Cleaned up functions to only exit at one point
1.0rc5
Moved includes to be in correct order
Fixed Q0 and Q1 (they were reversed)
1.0rc6
Add support for direct connection
1.0rc7
Fixed mislocated tcsetattr in serial.com:ser_init_conn
1.0rc8
Fixed bug in ip_thread (calling a null number would turn on ip_thread, but no connection, generating a dump
Fixed bugs in dce_set_flow_control
Fixed behavior of parms parsing for &Z (complex) and S (simple)
Defaulted code to not assume /dev/ttyS0 as serport
1.0rc9
Fixed bug that would stop first session if second call came in
Fixed outbound call not bringing DCD high.
1.0rc10
Added some more debugging information
Changed logging to go to stdout from stderr
Fixed Phone book matching function to use logger
1.0rc11
Added ip232 support with custom protocol for use with matching modified version of WinVICE 1.19
Added full support for Telnet Binary Transmission (RFC 856)
1.0rc12
Added code to hook SIG_IO to SIG_IGN. Newer versions of Linux are terminating when IO arrives.
1.0fz1 - Chris Osborn <fozztexx@fozztexx.com> 2016-May-24
Strip parity when looking for commands, generate matching parity on modem responses

29
Makefile Normal file
View File

@ -0,0 +1,29 @@
SRC=src
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
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
CC = gcc
DEF =
CFLAGS = -g $(DEF) -Wall
LDFLAGS = -lpthread
DEPEND = makedepend $(DEF) $(CFLAGS)
all: tcpser
#.o.c:
# $(CC) $(CFLAGS) -c $*.c
$(SRCS):
$(CC) $(CFLAGS) -c $*.c
tcpser: $(OBJS)
$(CC) $(LDFLAGS) -o $@ $(OBJS)
depend: $(SRCS)
$(DEPEND) $(SRCS)
clean:
-rm tcpser *.bak $(SRC)/*~ $(SRC)/*.o $(SRC)/*.bak core
# DO NOT DELETE THIS LINE -- make depend depends on it.

28
Makefile.solaris Normal file
View File

@ -0,0 +1,28 @@
SRC=src
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
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
CC = gcc
DEF =
CFLAGS = -O $(DEF) -Wall
LDFLAGS = -lpthread -lsocket -lnsl -lresolv
DEPEND = makedepend $(DEF) $(CFLAGS)
all: tcpser
#.o.c:
# $(CC) $(CFLAGS) -c $*.c
$(SRCS):
$(CC) $(CFLAGS) -c $*.c
tcpser: $(OBJS)
$(CC) $(LDFLAGS) -o $@ $(OBJS)
depend: $(SRCS)
$(DEPEND) $(SRCS)
clean:
-rm tcpser *.bak $(SRC)/*~ $(SRC)/*.o $(SRC)/*.bak core
# DO NOT DELETE THIS LINE -- make depend depends on it.

29
Makefile.win32 Normal file
View File

@ -0,0 +1,29 @@
SRC=src
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
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
CC = gcc
DEF =
CFLAGS = -O $(DEF) -Wall -DWIN32
LDFLAGS =
DEPEND = makedepend $(DEF) $(CFLAGS)
all: tcpser
#.o.c:
# $(CC) $(CFLAGS) -c $*.c
$(SRCS):
$(CC) $(CFLAGS) -c $*.c
tcpser: $(OBJS)
$(CC) $(LDFLAGS) -o $@ $(OBJS)
depend: $(SRCS)
$(DEPEND) $(SRCS)
clean:
-rm tcpser.exe *.bak $(SRC)/*~ $(SRC)/*.o $(SRC)/*.bak core
# DO NOT DELETE THIS LINE -- make depend depends on it.

234
README Normal file
View File

@ -0,0 +1,234 @@
README
Application:
TCPSER turns a PC serial port into an emulated Hayes compatible modem that
uses TCP/IP for incoming and outgoing connections. It can be used to allow
older applications and systems designed for modem use to operate on the
Internet. TCPSER supports all standard Hayes commands, and understands
extended and vendor proprietary commands (though it does not implement
many of them). TCPSER can be used for both inbound and outbound connections.
License:
TCPSER is distributed under the GPL 2.0 or later
Executable/Building:
UNIX/Linux/BSD:
Simply untar the archive into a directory, and use the appropriate make command
generate the exectutable. If unsure, try the default make command first.
Default/Linux: make
Solaris: make -f Makefile.solaris
*BSD: gmake
Windows 95/OSR2/98/SE/ME/NT/2000/XP/2003:
The application archive contains a pregenerated Windows 32 bit executable
(tcpser.exe). No compilation should be required, though the GNU toolchain and
the CYGWIN POSIX libraries can be used to regenerate the executable if desired:
Win32: make -f Makefile.win32
Note that at least the cygwin1.dll library is required to operate tcpser under
Windows. I recommend downloading a recent version from www.cygwin.com
This version of tcpser supports setting up an ip232 port instead of using a
real serial port. This is for use with the version of WinVICE 1.19 that has
the ACIA fix and ip232 support, allowing WinVICE to use tcpser as if it was
connected via a serial cable.
Operation:
tcpser -d <dev> -s <speed> -l <log_level> -t <tracing options> ...
-or-
tcpser -v <port> -s <speed> -l <log_level> -t <tracing options> ...
samples:
tcpser -d /dev/ttyS0 -s 38400 -l 7 -tsSiI -i "s0=1" -p 6400
Will start tcpser on ttyS0 at 38400 bps, level 7 logging, tracing of
inbound serial, outbound serial, inbound IP, outbound IP, init
modem to answer after 1 ring, and listen for incoming connections on port
6400
tcpser -v 25232 -s 38400 -l 4 -p 23
Will set up an ip232 port at 25232, report 38400 bps connections,
level 4 logging, and listen for incoming connections on port 23.
tcpser -h will provide additional information
tcpser can be configured to send the contents of a file upon:
connect ; -c -C
answer ; -a -A
no-answer ; -I
busy ; -B
inactivity-timeout ; -T
For connect and answer, there are separate options for sending a file to the
local serial connection (-c, -a) and the remote IP connection (-C, -A).
If tcpser connects to a telnet service, tcpser will negotiate the connection
using the telnet protocol. If telnet is detected, then tcpser will support
RFC 856 (Telnet Binary Transmission), so that 8-bit file transfers will
work correctly.
tcpser can be configured to support multiple serial/ip232 ports on one TCP/IP
port. Simply repeat the -s and -d/-v parameters on the command line for each
serial/ip232 port to be configured. Options s,S,a,A,c,C,I, and T will
"propagate" to subsequent connections, unless they are redefined. Defaults
for s and S are 38400. This configuration enables the operation of a
multi-line BBS on one TCP/IP port.
Frequently used addresses can be configured in the "phonebook", like so:
tcpser .... -nhome=jbrain.com:6400
This is also useful for systems that do not accept non-numeric phone numbers:
tcpser .... -n5551212=bestbbs.net
One can even "hide" a regular IP address or DNS entry by aliasing it to
something else:
tcpser .... -njbrain.com=bestbbs.com
At this point, phonebook support is very alpha, so use with care.
Emulation:
All of the standard Hayes commands should behave as expected. Some of
of the proprietary commands are not implemented, but should not cause
errors.
Examples:
ats0=1 ; set number of rings to answer
ata ; answer the line
ath0 ; hang up
ats12? ; query S register 12
ate0 ; turn off echo
at&k3 ; set flow control to RTS/CTS
atdtjbrain.com:6400 ; "dial" jbrain.com, port 6400 (defaults to port 23)
atdl ; "dial" last number
a/ ; repeat last command
Commands can be chained, as on a regular modem:
ats0=1z&c1&k3%f0s3=13dtjbrain.com
tcpser supports the Hayes break sequence semantics, so +++ should operate
correctly, even if the sequence of characters is used in normal data
transmissions.i
Cable:
tcpser can be used with a regular null modem cable, but it utilizes the DTR
line on the PC serial port to reflect the state of the DCD line as seen by the
target system. On a normal null-modem cable, DTR is mapped to DCD/DSR, which
implies DSR will also reflect the state of DCD on the target machine. However,
some systems (notably those utilizing the 6551 ACIA communication IC) will not
transmit unless DSR is held high. In this case, a quick qorkaround is to force
DCD to be held high by adding -i"&c0" to the tcpser parameter list. However,
this also prevents normal operation of the DCD line, which is needed by some
BBS systems. A more permanent solution is to construct a modified null-modem
cable or modify an existing cable to the following specifications:
PC Target
CTS-----RTS
RTS-----CTS
SND-----RCV
RCV-----SND
DTR-----DCD
DCD-+-+-DTR
| |
DSR-+ +-DSR
GND-----GND
This differs from a regular null-modem cable in that the target machine has DSR
looped to DTR, not to DCD. Note that this cable is directional.
Normally, the target machine will configure DSR to float to a high state if
unconnected. As well, PCs do not require a valid DSR line for operation. Thus,
a simpler cable can be constructed that is bi-directional:
CTS-----RTS
RTS-----CTS
SND-----RCV
RCV-----SND
DTR-----DCD
DCD-----DTR
GND-----GND
Unless there are issues, we recommend this simplified version, as it can be
installed in either direction.
As an even simpler solution, many have simply taken a normal rs232 DE-9F to
DE-9M cable and removed pin 6 from the male end (DSR). This is fine, but the
cable must be installed between the null modem adapter and the target machine:
PC ----- null-modem adapter ----- cable with pin 6 removed ------ target machine
Any other configuration will not work correctly.
Platform notes:
Win32 users should use /dev/ttyS0-3 for COM1-4. At present, using "com1:"
does not operate correctly.
Raymond Day sends the following Ubuntu 7.10 autorun scripts:
In:
/etc/init.d/
Make a file named something like tcpser with this code in it:
#!/bin/sh
# Start tcpser at 1200 boud on both RS232 ports for Q-Link Reloaded.
case "$1" in
'start')
tcpser -d /dev/ttyS0 -s 1200 -i"e0&k0&c0" -n"5551212"=66.135.39.36:5190&
tcpser -d /dev/ttyS1 -p 6401 -s 1200 -i"e0&k0&c0" -n"5551212"=66.135.39.36:5190&
;;
'stop')
;;
*)
echo "Usage: $0 { start | stop }"
;;
esac
exit 0
This has been tested on the following platforms:
Linux 2.4.20-8
Windows XP
Windows XP SP1
Slackware 10.0
Help:
tcpser has a small but active user community. Help can be found by asking a
question in comp.sys.cbm, on the NEWNet #c64friends IRC channel, or by emailing
the author.
-----------------------------------
Jim Brain
brain@jbrain.com
www.jbrain.com
The ip232 support was added by Anthony Tolle. For questions regarding that,
e-mail atolle@gcns.com

2
bridge Normal file
View File

@ -0,0 +1,2 @@
tcpser-1.0rc10/tcpser -d /dev/ttyS1 -s 1200 -i"e0" -tsS -n"5551212=ltshosting.net:5190"

452
src/bridge.c Normal file
View File

@ -0,0 +1,452 @@
#include <stdio.h>
#include <sys/socket.h> // for recv...
#include <unistd.h> // for read...
#include <stdlib.h> // for exit...
#include <sys/param.h>
#include <sys/time.h>
#include <pthread.h>
#include "util.h"
#include "debug.h"
#include "nvt.h"
#include "modem_core.h"
#include "ip.h"
//#include "serial.h"
#include "getcmd.h"
#include "bridge.h"
const char MDM_NO_ANSWER[] = "NO ANSWER\n";
//const char CONNECT_NOTICE[] = "\r\nCONNECTING...\r\n";
//const char TELNET_NOTICE[] = "TELNET MODE ENABLED\r\n";
int accept_connection(modem_config *cfg)
{
LOG_ENTER();
cfg->line_data.fd = ip_accept(cfg->line_data.sfd);
if (cfg->line_data.fd > -1) {
cfg->line_data.valid_conn = TRUE;
if (cfg->data.direct_conn == TRUE) {
cfg->conn_type = MDM_CONN_INCOMING;
mdm_off_hook(cfg);
cfg->cmd_mode = TRUE;
}
else {
//line_write(cfg,(char*)CONNECT_NOTICE,strlen(CONNECT_NOTICE));
cfg->rings = 0;
mdm_send_ring(cfg);
}
// tell parent I got it.
LOG(LOG_DEBUG, "Informing parent task that I am busy");
writePipe(cfg->data.mp[0][1], MSG_ACCEPTED);
}
LOG_EXIT();
return 0;
}
int parse_ip_data(modem_config *cfg, char *data, int len)
{
// I'm going to cheat and assume it comes in chunks.
int i = 0;
char ch;
char text[1025];
int text_len = 0;
if (cfg->line_data.first_char == TRUE) {
cfg->line_data.first_char = FALSE;
if ((data[0] == 0xff) || (data[0] == 0x1a)) {
//line_write(cfg,(char*)TELNET_NOTICE,strlen(TELNET_NOTICE));
LOG(LOG_INFO, "Detected telnet");
cfg->line_data.is_telnet = TRUE;
}
}
if (cfg->line_data.is_telnet == TRUE) {
while (i < len) {
ch = data[i];
if (NVT_IAC == ch) {
// what if we roll off the end?
ch = data[i + 1];
switch (ch) {
case NVT_WILL:
case NVT_DO:
case NVT_WONT:
case NVT_DONT:
/// again, overflow issues...
LOG(LOG_INFO, "Parsing nvt command");
parse_nvt_command(cfg->line_data.fd, &cfg->line_data.nvt_data, ch, data[i + 2]);
i += 3;
break;
case NVT_SB: // sub negotiation
// again, overflow...
i +=
parse_nvt_subcommand(cfg->line_data.fd, &cfg->line_data.nvt_data, data + i,
len - i);
break;
case NVT_IAC:
if (cfg->line_data.nvt_data.binary_recv)
text[text_len++] = NVT_IAC;
// fall through to skip this sequence
default:
// ignore...
i += 2;
}
}
else {
text[text_len++] = data[i++];
}
if (text_len == 1024) {
text[text_len] = 0;
// write to serial...
mdm_write(cfg, text, text_len);
text_len = 0;
}
}
if (text_len) {
text[text_len] = 0;
// write to serial...
mdm_write(cfg, text, text_len);
}
}
else {
mdm_write(cfg, data, len);
}
return 0;
}
void *ip_thread(void *arg)
{
modem_config *cfg = (modem_config *) arg;
int action_pending = FALSE;
fd_set readfs;
int max_fd;
int res = 0;
char buf[256];
int rc;
LOG_ENTER();
while (TRUE) {
FD_ZERO(&readfs);
FD_SET(cfg->data.cp[1][0], &readfs);
max_fd = cfg->data.cp[1][0];
if (action_pending == FALSE
&& cfg->conn_type != MDM_CONN_NONE
&& cfg->cmd_mode == FALSE
&& cfg->line_data.fd > -1 && cfg->line_data.valid_conn == TRUE) {
FD_SET(cfg->line_data.fd, &readfs);
max_fd = MAX(max_fd, cfg->line_data.fd);
}
max_fd++;
rc = select(max_fd, &readfs, NULL, NULL, NULL);
if (rc == -1) {
ELOG(LOG_WARN, "Select returned error");
// handle error
}
else {
// we got data
if (cfg->line_data.valid_conn == TRUE && FD_ISSET(cfg->line_data.fd, &readfs)) { // socket
LOG(LOG_DEBUG, "Data available on socket");
res = recv(cfg->line_data.fd, buf, sizeof(buf), 0);
if (0 >= res) {
LOG(LOG_INFO, "No socket data read, assume closed peer");
writePipe(cfg->data.cp[0][1], MSG_DISCONNECT);
action_pending = TRUE;
}
else {
LOG(LOG_DEBUG, "Read %d bytes from socket", res);
buf[res] = 0;
log_trace(TRACE_IP_IN, buf, res);
parse_ip_data(cfg, buf, res);
}
}
if (FD_ISSET(cfg->data.cp[1][0], &readfs)) { // pipe
res = read(cfg->data.cp[1][0], buf, sizeof(buf) - 1);
LOG(LOG_DEBUG, "IP thread notified");
action_pending = FALSE;
}
}
}
LOG_EXIT();
}
void *ctrl_thread(void *arg)
{
modem_config *cfg = (modem_config *) arg;
int status;
int new_status;
LOG_ENTER();
status = dce_get_control_lines(cfg);
while (status > -1) {
new_status = dce_check_control_lines(cfg);
if (new_status > -1 && status != new_status) {
// something changed
if ((status & MDM_CL_DTR_HIGH) != (new_status & MDM_CL_DTR_HIGH)) {
if ((new_status & MDM_CL_DTR_HIGH) == 0) {
LOG(LOG_INFO, "DTR has gone low");
writePipe(cfg->data.wp[0][1], MSG_DTR_DOWN);
}
else {
LOG(LOG_INFO, "DTR has gone high");
writePipe(cfg->data.wp[0][1], MSG_DTR_UP);
}
}
}
status = new_status;
}
LOG_EXIT();
// need to quit application, as status cannot be obtained.
exit(-1);
}
int spawn_ctrl_thread(modem_config *cfg)
{
int rc;
pthread_t thread_id;
rc = pthread_create(&thread_id, NULL, ctrl_thread, (void *) cfg);
LOG(LOG_ALL, "CTRL thread ID=%d", (int) thread_id);
if (rc < 0) {
ELOG(LOG_FATAL, "CTRL thread could not be started");
exit(-1);
}
return 0;
}
int spawn_ip_thread(modem_config *cfg)
{
int rc;
pthread_t thread_id;
rc = pthread_create(&thread_id, NULL, ip_thread, (void *) cfg);
LOG(LOG_ALL, "IP thread ID=%d", (int) thread_id);
if (rc < 0) {
ELOG(LOG_FATAL, "IP thread could not be started");
exit(-1);
}
return 0;
}
void *run_bridge(void *arg)
{
modem_config *cfg = (modem_config *) arg;
struct timeval timer;
struct timeval *ptimer;
int max_fd = 0;
fd_set readfs;
int res = 0;
char buf[256];
int rc = 0;
int last_conn_type;
int last_cmd_mode = cfg->cmd_mode;
LOG_ENTER();
if (-1 == pipe(cfg->data.wp[0])) {
ELOG(LOG_FATAL, "Control line watch task incoming IPC pipe could not be created");
exit(-1);
}
if (-1 == pipe(cfg->data.cp[0])) {
ELOG(LOG_FATAL, "IP thread incoming IPC pipe could not be created");
exit(-1);
}
if (-1 == pipe(cfg->data.cp[1])) {
ELOG(LOG_FATAL, "IP thread outgoing IPC pipe could not be created");
exit(-1);
}
spawn_ctrl_thread(cfg);
spawn_ip_thread(cfg);
mdm_set_control_lines(cfg);
strncpy(cfg->cur_line, cfg->config0, sizeof(cfg->cur_line));
last_conn_type = cfg->conn_type;
last_cmd_mode = cfg->cmd_mode;
cfg->allow_transmit = FALSE;
// call some functions behind the scenes
mdm_disconnect(cfg);
mdm_parse_cmd(cfg);
// if direct connection, and num length > 0, dial number.
if (cfg->data.direct_conn == TRUE) {
if (strlen(cfg->data.direct_conn_num) > 0 && cfg->data.direct_conn_num[0] != ':') {
// we have a direct number to connect to.
strncpy(cfg->dialno, cfg->data.direct_conn_num, sizeof(cfg->dialno));
if (0 != line_connect(cfg)) {
LOG(LOG_FATAL, "Cannot connect to Direct line address!");
// probably should exit...
exit(-1);
}
else {
cfg->conn_type = MDM_CONN_OUTGOING;
}
}
}
cfg->allow_transmit = TRUE;
for (;;) {
if (last_conn_type != cfg->conn_type) {
LOG(LOG_ALL, "Connection status change, handling");
//writePipe(cfg->data.mp[0][1],MSG_NOTIFY);
writePipe(cfg->data.cp[1][1], MSG_NOTIFY);
if (cfg->conn_type == MDM_CONN_OUTGOING) {
if (strlen(cfg->data.local_connect) > 0) {
writeFile(cfg->data.local_connect, cfg->line_data.fd);
}
if (strlen(cfg->data.remote_connect) > 0) {
writeFile(cfg->data.remote_connect, cfg->line_data.fd);
}
}
else if (cfg->conn_type == MDM_CONN_INCOMING) {
if (strlen(cfg->data.local_answer) > 0) {
writeFile(cfg->data.local_answer, cfg->line_data.fd);
}
if (strlen(cfg->data.remote_answer) > 0) {
writeFile(cfg->data.remote_answer, cfg->line_data.fd);
}
}
last_conn_type = cfg->conn_type;
}
if (last_cmd_mode != cfg->cmd_mode) {
writePipe(cfg->data.cp[1][1], MSG_NOTIFY);
last_cmd_mode = cfg->cmd_mode;
}
LOG(LOG_ALL, "Waiting for modem/control line/timer/socket activity");
LOG(LOG_ALL, "Command Mode=%d, Connection status=%d", cfg->cmd_mode, cfg->conn_type);
max_fd = MAX(cfg->data.mp[1][0], cfg->dce_data.fd);
max_fd = MAX(max_fd, cfg->data.wp[0][0]);
max_fd = MAX(max_fd, cfg->data.cp[0][0]);
FD_ZERO(&readfs);
FD_SET(cfg->data.mp[1][0], &readfs);
FD_SET(cfg->dce_data.fd, &readfs);
FD_SET(cfg->data.wp[0][0], &readfs);
FD_SET(cfg->data.cp[0][0], &readfs);
ptimer = NULL;
if (cfg->cmd_mode == FALSE) {
if (cfg->pre_break_delay == FALSE || cfg->break_len == 3) {
LOG(LOG_ALL, "Setting timer for break delay");
timer.tv_sec = 0;
timer.tv_usec = cfg->s[12] * 20000;
ptimer = &timer;
}
else if (cfg->pre_break_delay == TRUE && cfg->break_len > 0) {
LOG(LOG_ALL, "Setting timer for inter-break character delay");
timer.tv_sec = 1; // 1 second
timer.tv_usec = 0;
ptimer = &timer;
}
else if (cfg->s[30] != 0) {
LOG(LOG_ALL, "Setting timer for inactivity delay");
timer.tv_sec = cfg->s[30] * 10;
timer.tv_usec = 0;
ptimer = &timer;
}
}
else if (cfg->cmd_mode == TRUE && cfg->conn_type == MDM_CONN_NONE
&& cfg->line_data.valid_conn == TRUE) {
LOG(LOG_ALL, "Setting timer for rings");
timer.tv_sec = 4;
timer.tv_usec = 0;
ptimer = &timer;
}
max_fd++;
rc = select(max_fd, &readfs, NULL, NULL, ptimer);
if (rc == -1) {
ELOG(LOG_WARN, "Select returned error");
// handle error
}
else if (rc == 0) {
// timer popped.
if (cfg->cmd_mode == TRUE && cfg->conn_type == MDM_CONN_NONE
&& cfg->line_data.valid_conn == TRUE) {
if (cfg->s[0] == 0 && cfg->rings == 10) {
// not going to answer, send some data back to IP and disconnect.
if (strlen(cfg->data.no_answer) == 0) {
line_write(cfg, (char *) MDM_NO_ANSWER, strlen(MDM_NO_ANSWER));
}
else {
writeFile(cfg->data.no_answer, cfg->line_data.fd);
}
mdm_disconnect(cfg);
}
else
mdm_send_ring(cfg);
}
else
mdm_handle_timeout(cfg);
}
if (FD_ISSET(cfg->dce_data.fd, &readfs)) { // serial port
LOG(LOG_DEBUG, "Data available on serial port");
res = dce_read(cfg, buf, sizeof(buf));
LOG(LOG_DEBUG, "Read %d bytes from serial port", res);
if (res > 0) {
if (cfg->conn_type == MDM_CONN_NONE && cfg->off_hook == TRUE) {
// this handles the case where atdt goes off hook, but no
// connection
mdm_disconnect(cfg);
mdm_send_response(MDM_RESP_OK, cfg);
}
else {
mdm_parse_data(cfg, buf, res);
}
}
}
if (FD_ISSET(cfg->data.wp[0][0], &readfs)) { // control pipe
res = read(cfg->data.wp[0][0], buf, sizeof(buf) - 1);
buf[res] = 0;
LOG(LOG_DEBUG, "Received %c from Control line watch task", buf[0]);
switch (buf[0]) {
case MSG_DTR_DOWN:
// DTR drop, close any active connection and put
// in cmd_mode
mdm_disconnect(cfg);
break;
case MSG_DTR_UP:
break;
}
}
if (FD_ISSET(cfg->data.cp[0][0], &readfs)) { // ip thread pipe
res = read(cfg->data.cp[0][0], buf, sizeof(buf));
LOG(LOG_DEBUG, "Received %c from ip thread", buf[0]);
switch (buf[0]) {
case MSG_DISCONNECT:
if (cfg->data.direct_conn == TRUE) {
// what should we do here...
LOG(LOG_ERROR,
"Direct Connection Link broken, disconnecting and awaiting new direct connection");
cfg->data.direct_conn = FALSE;
mdm_disconnect(cfg);
cfg->data.direct_conn = TRUE;
}
else {
mdm_disconnect(cfg);
}
break;
}
}
if (FD_ISSET(cfg->data.mp[1][0], &readfs)) { // parent pipe
LOG(LOG_DEBUG, "Data available on incoming IPC pipe");
res = read(cfg->data.mp[1][0], buf, sizeof(buf));
switch (buf[0]) {
case MSG_ACCEPT: // accept connection.
accept_connection(cfg);
break;
}
}
}
LOG_EXIT();
}

20
src/bridge.h Normal file
View File

@ -0,0 +1,20 @@
#define MSG_NOT_ACTIVE 'a'
#define MSG_ACTIVE 'A'
#define MSG_NOT_LISTENING 'l'
#define MSG_LISTENING 'L'
#define MSG_ACCEPT '+'
#define MSG_ACCEPTED '+'
#define MSG_DTR_DOWN 'd'
#define MSG_DTR_UP 'D'
#define MSG_RTS_DOWN 'r'
#define MSG_RTS_UP 'R'
#define MSG_CD_DOWN 'c'
#define MSG_CD_UP 'c'
#define MSG_RNG_DOWN 'n'
#define MSG_RNG_UP 'N'
#define MSG_DISCONNECT 'D'
#define MSG_NOTIFY 'N'
int accept_connection(modem_config *);
int parse_ip_data(modem_config *cfg, char *data, int len);
void *run_bridge(void *arg);

156
src/dce.c Normal file
View File

@ -0,0 +1,156 @@
#include <termios.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include "debug.h"
#include "serial.h"
#include "modem_core.h"
#include "ip232.h" // needs modem_core.h
#include "dce.h"
int dce_init_config(modem_config *cfg)
{
return 0;
}
int dce_init_conn(modem_config *cfg)
{
int rc;
LOG_ENTER();
if (cfg->dce_data.is_ip232) {
rc = ip232_init_conn(cfg);
}
else {
rc = ser_init_conn(cfg->dce_data.tty, cfg->dte_speed);
cfg->dce_data.fd = rc;
}
LOG_EXIT();
return rc;
}
int dce_set_flow_control(modem_config *cfg, int opts)
{
int status = 0;
int rc = 0;
LOG_ENTER();
if (opts == 0) {
LOG(LOG_ALL, "Setting NONE flow control");
}
else {
if ((opts & MDM_FC_RTS) != 0) {
LOG(LOG_ALL, "Setting RTSCTS flow control");
status |= CRTSCTS;
}
if ((opts && MDM_FC_XON) != 0) {
status |= (IXON | IXOFF);
LOG(LOG_ALL, "Setting XON/XOFF flow control");
}
}
if (cfg->dce_data.is_ip232) {
rc = ip232_set_flow_control(cfg, status);
}
else {
rc = ser_set_flow_control(cfg->dce_data.fd, status);
}
LOG_EXIT()
return rc;
}
int dce_set_control_lines(modem_config *cfg, int state)
{
int status = 0;
int rc;
LOG_ENTER();
if ((state & MDM_CL_CTS_HIGH) != 0) {
LOG(LOG_ALL, "Setting CTS pin high");
status |= TIOCM_RTS;
}
else {
LOG(LOG_ALL, "Setting CTS pin low");
//status &= ~TIOCM_RTS;
}
if ((state & MDM_CL_DCD_HIGH) != 0) {
LOG(LOG_ALL, "Setting DCD pin high");
status |= TIOCM_DTR;
}
else {
LOG(LOG_ALL, "Setting DCD pin low");
//status &= ~TIOCM_DTR;
}
if (cfg->dce_data.is_ip232) {
rc = ip232_set_control_lines(cfg, status);
}
else {
rc = ser_set_control_lines(cfg->dce_data.fd, status);
}
LOG_EXIT();
return rc;
}
int dce_get_control_lines(modem_config *cfg)
{
int status;
int rc_status;
if (cfg->dce_data.is_ip232) {
status = ip232_get_control_lines(cfg);
}
else {
status = ser_get_control_lines(cfg->dce_data.fd);
}
if (status > -1) {
rc_status = ((status & TIOCM_DSR) != 0 ? MDM_CL_DTR_HIGH : 0);
}
else {
rc_status = status;
}
return rc_status;
}
int dce_check_control_lines(modem_config *cfg)
{
int status = 0;
int new_status = 0;
LOG_ENTER();
status = dce_get_control_lines(cfg);
new_status = status;
while (new_status > -1 && status == new_status) {
usleep(100000);
new_status = dce_get_control_lines(cfg);
}
LOG_EXIT();
return new_status;
}
int dce_write(modem_config *cfg, char data[], int len)
{
if (cfg->dce_data.is_ip232) {
return ip232_write(cfg, data, len);
}
return ser_write(cfg->dce_data.fd, data, len);
}
int dce_read(modem_config *cfg, char data[], int len)
{
if (cfg->dce_data.is_ip232) {
return ip232_read(cfg, data, len);
}
return ser_read(cfg->dce_data.fd, data, len);
}

16
src/dce.h Normal file
View File

@ -0,0 +1,16 @@
#ifndef DCE_H
#define DCE_H 1
int dce_init(void);
int dce_init_config(modem_config *cfg);
int dce_init_conn(modem_config *cfg);
int dce_set_flow_control(modem_config *cfg, int opts);
int dce_set_control_lines(modem_config *cfg, int state);
int dce_get_control_lines(modem_config *cfg);
int dce_check_control_lines(modem_config *cfg);
int dce_read(modem_config *cfg, char *data, int len);
int dce_write(modem_config *cfg, char *data, int len);
//int dce_check_for_break(modem_config *cfg, char ch, int chars_left);
#endif

141
src/debug.c Normal file
View File

@ -0,0 +1,141 @@
#include <stdlib.h> // for exit...
#include <time.h>
#include <pthread.h>
#include <stdio.h>
#define DEBUG_VARS 1 // need this so we don't get extern defs
#include "debug.h"
int log_level = 0;
FILE *log_file;
int trace_flags = 0;
char *trace_type[9]; // cheesy, but I can't think of another o(1) way
char *log_desc[LOG_TRACE + 1];
pthread_mutex_t log_mutex;
int log_init()
{
log_file = stdout;
log_level = 0;
trace_flags = 0;
trace_type[TRACE_MODEM_IN] = "RS<-";
trace_type[TRACE_MODEM_OUT] = "RS->";
trace_type[TRACE_IP_IN] = "IP<-";
trace_type[TRACE_IP_OUT] = "IP->";
log_desc[LOG_FATAL] = "FATAL";
log_desc[LOG_ERROR] = "ERROR";
log_desc[LOG_WARN] = "WARN";
log_desc[LOG_INFO] = "INFO";
log_desc[LOG_DEBUG] = "DEBUG";
log_desc[LOG_ENTER_EXIT] = "ENTER_EXIT";
log_desc[LOG_ALL] = "DEBUG_X";
log_desc[LOG_TRACE] = "";
if (-1 == pthread_mutex_init(&log_mutex, NULL)) {
perror("Could not create Log Mutex");
exit(-1);
}
return 0;
}
void log_set_file(FILE * a)
{
log_file = a;
}
void log_set_level(int a)
{
log_level = a;
}
void log_set_trace_flags(int a)
{
trace_flags = a;
}
int log_get_trace_flags()
{
return trace_flags;
}
void log_trace(int type, char *line, int len)
{
int i = 0;
int ch;
char data[64] = "\0";
char *dptr = NULL;
char text[17];
if (len == 0)
return;
if ((type & trace_flags) != 0) {
text[16] = 0;
for (i = 0; i < len; i++) {
if ((i % 16) == 0) {
// beginning of line
dptr = data;
sprintf(dptr, "%4.4x|", i);
}
ch = line[i];
sprintf(dptr + 5 + ((i % 16) * 3), "%2.2x", ch);
if (ch > 31 && ch < 127) {
text[i % 16] = ch;
}
else {
text[i % 16] = '.';
}
if ((i % 16) == 15) {
log_start(LOG_TRACE);
fprintf(log_file, "%s|%s|%s|", trace_type[type], data, text);
log_end();
}
else {
sprintf(dptr + 7 + ((i % 16) * 3), " ");
}
}
i = i % 16;
if (i > 0) {
for (; i < 16; i++) {
sprintf(dptr + 5 + ((i % 16) * 3), " ");
if ((i % 16) != 15) {
sprintf(dptr + 7 + ((i % 16) * 3), " ");
}
text[i % 16] = ' ';
}
log_start(LOG_TRACE);
fprintf(log_file, "%s|%s|%s|", trace_type[type], data, text);
}
log_end();
}
}
void log_start(int level)
{
char t[23];
time_t now;
if (-1 == pthread_mutex_lock(&log_mutex)) {
perror("Could not lock the log mutex");
}
else {
// we have the lock.
now = time(NULL);
strftime(t, 22, "%Y-%m-%d %H:%M:%S", localtime(&now));
fprintf(log_file, "%s:%5.5d:%s:", t, (int) pthread_self(), log_desc[level]);
//free(t);
}
}
void log_end()
{
fprintf(log_file, "\n");
fflush(log_file);
if (-1 == pthread_mutex_unlock(&log_mutex)) {
perror("Could not lock the log mutex");
}
}

69
src/debug.h Normal file
View File

@ -0,0 +1,69 @@
#ifndef DEBUG_H
#define DEBUG_H 1
#define LOG_TRACE 10
#define LOG_ALL 7
#define LOG_ENTER_EXIT 6
#define LOG_DEBUG 5
#define LOG_INFO 4
#define LOG_WARN 3
#define LOG_ERROR 2
#define LOG_FATAL 1
#define LOG_NONE 0
#define TRACE_MODEM_IN 1
#define TRACE_MODEM_OUT 2
#define TRACE_IP_IN 4
#define TRACE_IP_OUT 8
#include <stdio.h> // needed for strerror
#include <string.h> // needed for strerror
#include <errno.h> // needed for errno
#if __STDC_VERSION__ < 199901L
#if __GNUC__ >= 2
#define __func__ __FUNCTION__
#else
#define __func__ "<unknown>"
#endif
#endif
#define LOG(a,args...) do { \
if(a <= log_level) { \
log_start(a); \
fprintf(log_file,args); \
log_end(); \
} \
} while(0)
#define ELOG(a,args...) do { \
if(a <= log_level) { \
log_start(a); \
fprintf(log_file,args); \
fprintf(log_file," (%s)\n",strerror(errno)); \
log_end(); \
} \
} while(0)
#define LOG_ENTER() LOG(LOG_ENTER_EXIT,"Entering %s function",__func__);
#define LOG_EXIT() LOG(LOG_ENTER_EXIT,"Exitting %s function",__func__);
int log_init(void);
void log_set_file(FILE * a);
void log_set_level(int a);
int log_get_trace_flags();
void log_set_trace_flags(int a);
void log_trace(int type, char *line, int len);
void log_start(int level);
void log_end();
#endif
#ifndef DEBUG_VARS
#define DEBUG_VARS 1
#include <stdio.h>
extern int log_level;
extern FILE *log_file;
#endif

275
src/getcmd.c Normal file
View File

@ -0,0 +1,275 @@
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include "getcmd.h"
int getData(char line[], int *index, int len, int *data_start, int *data_end, int complex_parse)
{
int alpha = FALSE;
int done = FALSE;
*data_start = *index;
while (*index < len && done != TRUE) {
// I'm going to assume either
// a number
// a string with a space
switch (line[*index]) {
case ' ':
if (!complex_parse && *index != *data_start) {
// leave space, next call will skip it.
done = TRUE;
}
else if (*index != *data_start) {
// we are complex, add the space and continue.
(*index)++;
}
else {
// we have not started, eat space and continue.
(*index)++;
*data_start = *index;
}
break;
case 0:
done = TRUE;
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9': // isnum
(*index)++;
break;
default:
if (!complex_parse && *index != *data_start && 0 == alpha) {
// we were a number, but we've hit an alpha 'S0=137S...'
done = TRUE;
}
else {
(*index)++;
alpha = TRUE;
}
break;
}
}
*data_end = (*index);
return 0;
}
int getNumber(char line[], int *index, int len)
{
int num = 0;
int found = FALSE;
while (*index < len && 0 != isdigit(line[*index])) {
num = num * 10 + line[(*index)++] - '0';
found = 1;
}
if (FALSE == found)
return -1;
return num;
}
int skip(char line[], int *index, int len, char ch)
{
while (*index < len && ch == line[*index])
(*index)++;
return 0;
}
int getCommand(char line[], int flags, int *index, int *num, int len)
{
int cmd = line[(*index)++];
*num = getNumber(line, index, len);
return cmd;
}
int parseCommand(char line[], int flags, int *index, int *num, int len)
{
int cmd = getCommand(line, flags, index, num, len);
if (0 < cmd && 0 > *num)
*num = 0;
return toupper(cmd) | flags;
}
int parseRegister(char line[],
int flags,
int *index,
int *num, int len, int *data_start, int *data_end, int complex_parse)
{
// need to handle S<num>?, which queries that S register.
int cmd = 0;
cmd = getCommand(line, flags, index, num, len);
if (0 > num)
return AT_CMD_ERR;
skip(line, index, len, ' ');
if (len == *index)
return AT_CMD_ERR;
switch (line[(*index)++]) {
case '=':
// set a register
skip(line, index, len, ' ');
if (0 > getData(line, index, len, data_start, data_end, complex_parse))
return AT_CMD_ERR;
break;
case '?':
// query a register
flags |= AT_CMD_FLAG_QUERY;
if (*num < 0)
*num = 0;
break;
default:
return AT_CMD_ERR;
}
return toupper(cmd) | flags;
}
int getcmd(char line[], int *index, int *num, int *data_start, int *data_end)
{
int len = 0;
int cmd = AT_CMD_END;
*num = 0;
*data_start = 0;
*data_end = 0;
if (line == NULL)
return AT_CMD_NONE;
len = strlen(line);
while (*index < len) {
cmd = toupper(line[*index]);
switch (cmd) {
case ' ':
break;
case 0:
return AT_CMD_END;
case '%':
(*index)++;
while (*index < len) {
switch (toupper(line[*index])) {
case ' ':
break;
case 0:
return AT_CMD_ERR;
default:
return parseCommand(line, AT_CMD_FLAG_PRO_PCT, index, num, len);
}
(*index)++;
}
break;
case '\\':
(*index)++;
while (*index < len) {
switch (toupper(line[*index])) {
case ' ':
break;
case 0:
return AT_CMD_ERR;
default:
return parseCommand(line, AT_CMD_FLAG_PRO_BACK, index, num, len);
}
(*index)++;
}
break;
case ':':
(*index)++;
while (*index < len) {
switch (toupper(line[*index])) {
case ' ':
break;
case 0:
return AT_CMD_ERR;
default:
return parseCommand(line, AT_CMD_FLAG_PRO_COLON, index, num, len);
}
(*index)++;
}
break;
case '-':
(*index)++;
while (*index < len) {
switch (toupper(line[*index])) {
case ' ':
break;
case 0:
return AT_CMD_ERR;
default:
return parseCommand(line, AT_CMD_FLAG_PRO_MINUS, index, num, len);
}
(*index)++;
}
break;
case '&':
(*index)++;
while (*index < len) {
switch (toupper(line[*index])) {
case ' ':
break;
case 0:
return AT_CMD_ERR;
case 'Z':
return parseRegister(line, AT_CMD_FLAG_EXT, index, num, len, data_start, data_end,
TRUE);
default:
return parseCommand(line, AT_CMD_FLAG_EXT, index, num, len);
}
(*index)++;
}
break;
case 'D': // handle Dialing.
(*index)++;
*num = 0;
while (*index < len) {
switch (toupper(line[*index])) {
case 0:
return cmd;
case 'T':
case 'P':
case 'L':
*num = toupper(line[*index]);
(*index)++;
default:
getData(line, index, len, data_start, data_end, TRUE);
return cmd;
}
(*index)++;
}
return cmd;
case 'S':
return parseRegister(line, AT_CMD_FLAG_BAS, index, num, len, data_start, data_end, FALSE);
default:
return parseCommand(line, AT_CMD_FLAG_BAS, index, num, len);
}
(*index)++;
}
return cmd;
}
int main_getcmd(int argc, char **argv)
{
char data[] = "DT 555-1212";
int index = 0, num = 0, start = 0, end = 0;
int cmd = 0;
while (cmd != AT_CMD_END) {
cmd = getcmd(data, &index, &num, &start, &end);
printf("Cmd: %c Index: %d Num: %d Start: %d End: %d\n", cmd, index, num, start, end);
}
return 0;
}

28
src/getcmd.h Normal file
View File

@ -0,0 +1,28 @@
#define AT_CMD_END -1
#define AT_CMD_ERR -2
#define AT_CMD_NONE -4
#define AT_CMD_IERR -3
#define AT_CMD_FLAG_BAS 0
#define AT_CMD_FLAG_EXT 256
#define AT_CMD_FLAG_PRO_PCT 512
#define AT_CMD_FLAG_PRO_BACK 1024
#define AT_CMD_FLAG_PRO_MINUS 2048
#define AT_CMD_FLAG_PRO_DOLLAR 4096
#define AT_CMD_FLAG_PRO_COLON 8192
#define AT_CMD_FLAG_QUERY 32768
#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif
int getData(char line[], int *index, int len, int *data_start, int *data_end, int simple_parse);
int getNumber(char line[], int *index, int len);
int skip(char line[], int *index, int len, char ch);
int getCommand(char line[], int flags, int *index, int *num, int len);
int parseCommand(char line[], int flags, int *index, int *num, int len);
int parseRegister(char line[],
int flags,
int *index,
int *num, int len, int *data_start, int *data_end, int simple_parse);
int getcmd(char line[], int *index, int *num, int *data_start, int *data_end);

191
src/init.c Normal file
View File

@ -0,0 +1,191 @@
#include <stdio.h>
#include <stdlib.h> // for exit,atoi
#include <unistd.h>
#include "debug.h"
#include "phone_book.h"
#include "init.h"
void print_help(char *name)
{
fprintf(stderr, "Usage: %s <parameters>\n", name);
fprintf(stderr, " -p port to listen on (defaults to 6400)\n");
fprintf(stderr, " -t trace flags: (can be combined)\n");
fprintf(stderr, " 's' = modem input\n");
fprintf(stderr, " 'S' = modem output\n");
fprintf(stderr, " 'i' = IP input\n");
fprintf(stderr, " 'I' = IP input\n");
fprintf(stderr, " -l 0 (NONE), 1 (FATAL) - 7 (DEBUG_X) (defaults to 0)\n");
fprintf(stderr, " -L log file (defaults to stderr)\n");
fprintf(stderr, "\n");
fprintf(stderr, " The following can be repeated for each modem desired\n");
fprintf(stderr, " (-s, -S, and -i will apply to any subsequent device if not set again)\n");
fprintf(stderr, "\n");
fprintf(stderr, " -d serial device (e.g. /dev/ttyS0). Cannot be used with -v\n");
fprintf(stderr, " -v tcp port for VICE RS232 (e.g. 25232). Cannot be used with -d\n");
fprintf(stderr, " -s serial port speed (defaults to 38400)\n");
fprintf(stderr, " -S speed modem will report (defaults to -s value)\n");
fprintf(stderr, " -I invert DCD pin\n");
fprintf(stderr, " -n add phone entry (number=replacement)\n");
fprintf(stderr, " -a filename to send to local side upon answer\n");
fprintf(stderr, " -A filename to send to remote side upon answer\n");
fprintf(stderr, " -c filename to send to local side upon connect\n");
fprintf(stderr, " -C filename to send to remote side upon connect\n");
fprintf(stderr, " -N filename to send when no answer\n");
fprintf(stderr, " -B filename to send when modem(s) busy\n");
fprintf(stderr, " -T filename to send upon inactivity timeout\n");
fprintf(stderr,
" -i modem init string (defaults to '', leave off 'at' prefix when specifying)\n");
fprintf(stderr,
" -D direct connection (follow with hostname:port for caller, : for receiver)\n");
exit(1);
}
int init(int argc,
char **argv,
modem_config cfg[], int max_modem, int *port, char *all_busy, int all_busy_len)
{
int i = 0;
int j = 0;
int opt = 0;
int trace_flags = 0;
char *tok;
int dce_set = FALSE;
int tty_set = FALSE;
LOG_ENTER();
*port = 6400;
mdm_init_config(&cfg[0]);
cfg[0].dte_speed = 38400;
cfg[0].dce_speed = 38400;
while (opt > -1 && i < max_modem) {
opt = getopt(argc, argv, "p:s:S:d:v:hw:i:Il:L:t:n:a:A:c:C:N:B:T:D:");
switch (opt) {
case 't':
trace_flags = log_get_trace_flags();
for (j = 0; j < strlen(optarg); j++) {
switch (optarg[j]) {
case 's':
trace_flags |= TRACE_MODEM_IN;
break;
case 'S':
trace_flags |= TRACE_MODEM_OUT;
break;
case 'i':
trace_flags |= TRACE_IP_IN;
break;
case 'I':
trace_flags |= TRACE_IP_OUT;
break;
}
log_set_trace_flags(trace_flags);
}
break;
case 'a':
strncpy(cfg[i].data.local_answer, optarg, sizeof(cfg[i].data.local_answer));
break;
case 'A':
strncpy(cfg[i].data.remote_answer, optarg, sizeof(cfg[i].data.remote_answer));
break;
case 'c':
strncpy(cfg[i].data.local_connect, optarg, sizeof(cfg[i].data.local_connect));
break;
case 'C':
strncpy(cfg[i].data.remote_connect, optarg, sizeof(cfg[i].data.remote_connect));
break;
case 'B':
strncpy(all_busy, optarg, all_busy_len);
break;
case 'N':
strncpy(cfg[i].data.no_answer, optarg, sizeof(cfg[i].data.no_answer));
break;
case 'T':
strncpy(cfg[i].data.inactive, optarg, sizeof(cfg[i].data.inactive));
break;
case 'i':
strncpy(cfg[i].config0, optarg, 255);
break;
case 'I':
cfg[i].invert_dcd = TRUE;
break;
case 'p':
*port = (atoi(optarg));
break;
case 'n':
tok = strtok(optarg, "=");
pb_add(tok, strtok(NULL, "="));
break;
case 'l':
log_set_level(atoi(optarg));
break;
case 'L':
log_set_file(fopen(optarg, "w+"));
// should check to see if an error occurred...
break;
case 's':
cfg[i].dte_speed = atoi(optarg);
LOG(LOG_ALL, "Setting DTE speed to %d", cfg[i].dte_speed);
if (dce_set == FALSE)
cfg[i].dce_speed = cfg[i].dte_speed;
break;
case '?':
case 'h':
print_help(argv[0]);
break;
case 'd':
case 'v':
if (tty_set) {
if (++i < max_modem) {
dce_set = FALSE;
mdm_init_config(&cfg[i]);
cfg[i].dte_speed = cfg[i - 1].dte_speed;
cfg[i].dce_speed = cfg[i - 1].dce_speed;
cfg[i].dce_data.is_ip232 = FALSE;
strncpy(cfg[i].config0, cfg[i - 1].config0, sizeof(cfg[i].config0));
strncpy(cfg[i].data.local_connect, cfg[i - 1].data.local_connect,
sizeof(cfg[i].data.local_connect));
strncpy(cfg[i].data.remote_connect, cfg[i - 1].data.remote_connect,
sizeof(cfg[i].data.remote_connect));
strncpy(cfg[i].data.local_answer, cfg[i - 1].data.local_answer,
sizeof(cfg[i].data.local_answer));
strncpy(cfg[i].data.remote_answer, cfg[i - 1].data.remote_answer,
sizeof(cfg[i].data.remote_answer));
strncpy(cfg[i].data.no_answer, cfg[i - 1].data.no_answer,
sizeof(cfg[i].data.no_answer));
strncpy(cfg[i].data.inactive, cfg[i - 1].data.inactive, sizeof(cfg[i].data.inactive));
}
else {
LOG(LOG_WARN, "Maximum modems defined - ignoring extra");
break;
}
}
strncpy(cfg[i].dce_data.tty, optarg, sizeof(cfg[i].dce_data.tty));
cfg[i].dce_data.is_ip232 = ('v' == opt);
tty_set = TRUE;
break;
case 'S':
cfg[i].dce_speed = atoi(optarg);
dce_set = TRUE;
break;
case 'D':
cfg[i].data.direct_conn = TRUE;
strncpy(cfg[i].data.direct_conn_num, optarg, sizeof(cfg[i].data.direct_conn_num));
break;
}
}
if (tty_set) {
if (i < max_modem)
++i;
}
else {
// no modems defined
LOG(LOG_FATAL, "No modems defined");
print_help(argv[0]);
}
LOG(LOG_DEBUG, "Read configuration for %i serial port(s)", i);
LOG_EXIT();
return i;
}

11
src/init.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif
#include "modem_core.h"
void print_help(char *name);
int init(int argc,
char **argv,
modem_config cfg[], int max_modem, int *port, char *all_busy, int all_busy_len);

175
src/ip.c Normal file
View File

@ -0,0 +1,175 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <unistd.h> // for read...
#include <stdlib.h> // for atoi...
#include "debug.h"
#include "ip.h"
const int BACK_LOG = 5;
int ip_init_server_conn(int port)
{
int sSocket = 0, on = 0, rc = 0;
struct sockaddr_in serverName = { 0 };
LOG_ENTER();
LOG(LOG_DEBUG, "Creating server socket");
sSocket = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
if (-1 == sSocket) {
ELOG(LOG_FATAL, "Server socket could not be created");
}
else {
/*
* turn off bind address checking, and allow
* port numbers to be reused - otherwise
* the TIME_WAIT phenomenon will prevent
* binding to these addreG.
*/
on = 1;
rc = setsockopt(sSocket, SOL_SOCKET, SO_REUSEADDR, (const char *) &on, sizeof(on)
);
if (-1 == rc) {
ELOG(LOG_ERROR, "bind address checking could not be turned off");
}
serverName.sin_addr.s_addr = htonl(INADDR_ANY);
serverName.sin_family = AF_INET;
/* network-order */
serverName.sin_port = htons(port);
LOG(LOG_DEBUG, "Binding server socket to port %d", port);
rc = bind(sSocket, (struct sockaddr *) &serverName, sizeof(serverName)
);
if (-1 == rc) {
ELOG(LOG_FATAL, "Server socket could not be bound to port");
sSocket = -1;
}
else {
LOG(LOG_INFO, "Server socket bound to port");
rc = listen(sSocket, BACK_LOG);
LOG(LOG_INFO, "Server socket listening for connections");
if (-1 == rc) {
ELOG(LOG_FATAL, "Server socket could not listen on port");
sSocket = -1;
}
}
}
LOG_EXIT();
return sSocket;
}
int ip_connect(char addy[])
{
struct sockaddr_in pin;
struct in_addr cin_addr;
struct hostent *hp;
int sd = 0;
int port = 23;
char *address;
char *tmp;
LOG_ENTER();
address = (char *) strtok(addy, ":");
tmp = (char *) strtok((char *) 0, ":");
if (tmp != NULL && strlen(tmp) > 0) {
port = atoi(tmp);
}
LOG(LOG_DEBUG, "Calling %s", addy);
memset(&pin, 0, sizeof(pin));
/* go find out about the desired host machine */
if ((hp = gethostbyname(address)) == 0) {
// well, not a DNS entry... Treat as IP...
if (1 != inet_aton(address, &cin_addr)) {
ELOG(LOG_ERROR, "Host %s was invalid", addy);
return -1;
}
}
else {
cin_addr = *((struct in_addr *) (hp->h_addr));
}
pin.sin_family = AF_INET;
pin.sin_addr.s_addr = cin_addr.s_addr;
pin.sin_port = htons(port);
/* grab an Internet domain socket */
if ((sd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
ELOG(LOG_ERROR, "could not create client socket");
return -1;
}
/* connect to PORT on HOST */
if (connect(sd, (struct sockaddr *) &pin, sizeof(pin)) == -1) {
ELOG(LOG_ERROR, "could not connect to address");
return -1;
}
LOG(LOG_INFO, "Connection to %s established", addy);
LOG_EXIT();
return sd;
}
int ip_accept(int sSocket)
{
struct sockaddr_in clientName = { 0 };
socklen_t clientLength = sizeof(clientName);
int cSocket = -1;
LOG_ENTER();
(void) memset(&clientName, 0, sizeof(clientName));
cSocket = accept(sSocket, (struct sockaddr *) &clientName, &clientLength);
if (-1 == cSocket) {
ELOG(LOG_ERROR, "Could not accept incoming connection");
return -1;
}
if (-1 == getpeername(cSocket, (struct sockaddr *) &clientName, &clientLength)) {
ELOG(LOG_WARN, "Could not obtain peer name");
}
else {
LOG(LOG_INFO, "Connection accepted from %s", inet_ntoa(clientName.sin_addr)
);
}
LOG_EXIT();
return cSocket;
}
int ip_disconnect(int fd)
{
if (fd > -1)
close(fd);
return 0;
}
int ip_write(int fd, char *data, int len)
{
log_trace(TRACE_IP_OUT, data, len);
return write(fd, data, len);
}
int ip_read(int fd, char *data, int len)
{
int res;
res = recv(fd, data, len, 0);
log_trace(TRACE_IP_IN, data, res);
return res;
}

17
src/ip.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef IP_H
#define IP_H
#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif
int ip_init(void);
int ip_init_server_conn(int port);
int ip_connect(char addy[]);
int ip_accept(int sSocket);
int ip_disconnect(int fd);
int ip_write(int fd, char *data, int len);
int ip_read(int fd, char *data, int len);
#endif

259
src/ip232.c Normal file
View File

@ -0,0 +1,259 @@
#include <sys/socket.h> // for recv...
#include <stdlib.h> // for exit...
#include <sys/file.h>
#include <unistd.h>
#include <termios.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <pthread.h>
#include "util.h"
#include "debug.h"
#include "modem_core.h"
#include "ip.h"
#include "ip232.h"
void *ip232_thread(void *arg)
{
modem_config *cfg = (modem_config *) arg;
int accept_pending = FALSE;
int rc;
int res = 0;
char buf[256];
fd_set readfs;
int max_fd = 0;
int cSocket;
LOG_ENTER();
for (;;) {
FD_ZERO(&readfs);
FD_SET(cfg->dce_data.dp[1][0], &readfs);
max_fd = cfg->dce_data.dp[1][0];
if (accept_pending == FALSE) {
FD_SET(cfg->dce_data.sSocket, &readfs);
max_fd = MAX(max_fd, cfg->dce_data.sSocket);
}
LOG(LOG_ALL, "Waiting for incoming ip232 connections");
rc = select(max_fd + 1, &readfs, NULL, NULL, NULL);
if (rc < 0) {
// handle error
}
else {
if (FD_ISSET(cfg->dce_data.dp[1][0], &readfs)) { // pipe
res = read(cfg->dce_data.dp[1][0], buf, sizeof(buf) - 1);
LOG(LOG_DEBUG, "ip232 thread notified");
accept_pending = FALSE;
}
if (FD_ISSET(cfg->dce_data.sSocket, &readfs)) { // ip connection
if (cfg->dce_data.ip232_is_connected) {
LOG(LOG_DEBUG, "Already have ip232 connection, rejecting new");
// already have a connection... accept and close
cSocket = ip_accept(cfg->dce_data.sSocket);
if (cSocket > -1) {
close(cSocket);
}
}
else {
LOG(LOG_DEBUG, "Incoming ip232 connection");
writePipe(cfg->dce_data.dp[0][1], MSG_ACCEPT);
accept_pending = TRUE;
}
}
}
}
LOG_EXIT();
}
int spawn_ip232_thread(modem_config *cfg)
{
int rc;
pthread_t thread_id;
rc = pthread_create(&thread_id, NULL, ip232_thread, (void *) cfg);
LOG(LOG_ALL, "ip232 thread ID=%d", (int) thread_id);
if (rc < 0) {
ELOG(LOG_FATAL, "ip232 thread could not be started");
exit(-1);
}
return 0;
}
int ip232_init_conn(modem_config *cfg)
{
int rc = -1;
int port;
LOG_ENTER();
LOG(LOG_INFO, "Opening ip232 device");
port = atoi(cfg->dce_data.tty);
rc = ip_init_server_conn(port);
if (rc < 0) {
ELOG(LOG_FATAL, "Could not initialize ip232 server socket");
exit(-1);
}
if (-1 == pipe(cfg->dce_data.dp[0])) {
ELOG(LOG_FATAL, "ip232 thread incoming IPC pipe could not be created");
exit(-1);
}
if (-1 == pipe(cfg->dce_data.dp[1])) {
ELOG(LOG_FATAL, "ip232 thread outgoing IPC pipe could not be created");
exit(-1);
}
cfg->dce_data.sSocket = rc;
cfg->dce_data.ip232_is_connected = FALSE;
cfg->dce_data.fd = cfg->dce_data.dp[0][0];
spawn_ip232_thread(cfg);
LOG(LOG_INFO, "ip232 device configured");
LOG_EXIT();
return rc;
}
int ip232_set_flow_control(modem_config *cfg, int status)
{
return 0;
}
int ip232_get_control_lines(modem_config *cfg)
{
int status = 0;
if (cfg->dce_data.ip232_is_connected && cfg->dce_data.ip232_dtr) {
status |= TIOCM_DSR;
}
return status;
}
int ip232_set_control_lines(modem_config *cfg, int state)
{
int dcd;
char cmd[2];
if (cfg->dce_data.ip232_is_connected) {
dcd = (state & TIOCM_DTR) ? TRUE : FALSE;
if (dcd != cfg->dce_data.ip232_dcd) {
cfg->dce_data.ip232_dcd = dcd;
cmd[0] = 255;
cmd[1] = dcd ? 1 : 0;
ip_write(cfg->dce_data.fd, cmd, sizeof(cmd));
}
}
return 0;
}
int ip232_write(modem_config *cfg, char *data, int len)
{
int retval;
int i = 0;
int double_iac = FALSE;
char text[1024];
int text_len = 0;
log_trace(TRACE_MODEM_OUT, data, len);
retval = len;
if (cfg->dce_data.ip232_is_connected) {
while (i < len) {
if (double_iac) {
text[text_len++] = 255;
double_iac = FALSE;
i++;
}
else {
if (255 == data[i]) {
text[text_len++] = 255;
double_iac = TRUE;
}
else {
text[text_len++] = data[i++];
}
}
if (text_len == sizeof(text)) {
retval = ip_write(cfg->dce_data.fd, text, text_len);
text_len = 0;
}
}
if (text_len) {
retval = ip_write(cfg->dce_data.fd, text, text_len);
}
}
return retval;
}
int ip232_read(modem_config *cfg, char *data, int len)
{
int res;
int rc;
char buf[256];
int i = 0;
char ch;
int text_len = 0;
LOG_ENTER();
if (len > sizeof(buf)) {
LOG(LOG_FATAL, "ip232_read: len > sizeof(buf)");
exit(-1);
}
if (cfg->dce_data.ip232_is_connected) {
res = recv(cfg->dce_data.fd, buf, len, 0);
if (0 >= res) {
LOG(LOG_INFO, "No ip232 socket data read, assume closed peer");
ip_disconnect(cfg->dce_data.fd);
cfg->dce_data.fd = cfg->dce_data.dp[0][0];
cfg->dce_data.ip232_is_connected = FALSE;
}
else {
LOG(LOG_DEBUG, "Read %d bytes from ip232 socket", res);
log_trace(TRACE_MODEM_IN, buf, res);
while (i < res) {
ch = buf[i];
if (cfg->dce_data.ip232_iac) {
cfg->dce_data.ip232_iac = FALSE;
switch (ch) {
case 0:
cfg->dce_data.ip232_dtr = FALSE;
break;
case 1:
cfg->dce_data.ip232_dtr = TRUE;
break;
case 255:
data[text_len++] = 255;
break;
}
}
else {
if (255 == ch) {
cfg->dce_data.ip232_iac = TRUE;
}
else {
data[text_len++] = ch;
}
}
i++;
}
}
}
else {
// not connected
res = read(cfg->dce_data.dp[0][0], buf, sizeof(buf));
switch (buf[0]) {
case MSG_ACCEPT: // accept connection.
LOG(LOG_INFO, "Accepting ip232 connection...");
rc = ip_accept(cfg->dce_data.sSocket);
if (res > -1) {
cfg->dce_data.fd = rc;
cfg->dce_data.ip232_is_connected = TRUE;
cfg->dce_data.ip232_dtr = FALSE;
cfg->dce_data.ip232_dcd = FALSE;
writePipe(cfg->dce_data.dp[1][1], MSG_ACCEPTED);
}
break;
}
}
LOG_EXIT();
return text_len;
}

19
src/ip232.h Normal file
View File

@ -0,0 +1,19 @@
#ifndef IP232_H
#define IP232_H 1
#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif /* */
#define MSG_ACCEPT '+'
#define MSG_ACCEPTED '+'
int ip232_init_conn(modem_config *);
int ip232_set_flow_control(modem_config *, int status);
int ip232_get_control_lines(modem_config *);
int ip232_set_control_lines(modem_config *, int state);
int ip232_write(modem_config *, char *data, int len);
int ip232_read(modem_config *, char *data, int len);
#endif /* */

106
src/line.c Normal file
View File

@ -0,0 +1,106 @@
#include "debug.h"
#include "modem_core.h"
#include "phone_book.h"
#include "ip.h"
#include "bridge.h"
#include "line.h"
int line_init_config(modem_config *cfg)
{
cfg->line_data.fd = -1;
cfg->line_data.is_telnet = FALSE;
cfg->line_data.first_char = TRUE;
cfg->line_data.valid_conn = FALSE;
nvt_init_config(&cfg->line_data.nvt_data);
return 0;
}
int line_write(modem_config *cfg, char *data, int len)
{
int retval;
int i = 0;
int double_iac = FALSE;
char text[1024];
int text_len = 0;
if (cfg->line_data.is_telnet && cfg->line_data.nvt_data.binary_xmit) {
retval = 0;
while (i < len) {
if (double_iac) {
text[text_len++] = NVT_IAC;
double_iac = FALSE;
i++;
}
else {
if (NVT_IAC == data[i]) {
text[text_len++] = NVT_IAC;
double_iac = TRUE;
}
else {
text[text_len++] = data[i++];
}
}
if (text_len == 1024) {
retval = ip_write(cfg->line_data.fd, text, text_len);
text_len = 0;
}
}
if (text_len) {
retval = ip_write(cfg->line_data.fd, text, text_len);
}
return retval;
}
return ip_write(cfg->line_data.fd, data, len);
}
int line_listen(modem_config *cfg)
{
return 0;
}
int line_off_hook(modem_config *cfg)
{
return 0;
}
int line_connect(modem_config *cfg)
{
char *addy = cfg->dialno;
LOG(LOG_INFO, "Connecting");
addy = pb_search(addy);
cfg->line_data.fd = ip_connect(addy);
if (cfg->line_data.fd > -1) {
LOG(LOG_ALL, "Connected to %s", addy);
cfg->line_data.valid_conn = TRUE;
return 0;
}
else {
LOG(LOG_ALL, "Could not connect to %s", addy);
cfg->line_data.valid_conn = FALSE;
return -1;
}
}
int line_disconnect(modem_config *cfg)
{
LOG(LOG_INFO, "Disconnecting");
if (cfg->data.direct_conn == TRUE) {
LOG(LOG_INFO, "Direct connection active, maintaining link");
return -1;
}
else {
cfg->line_data.is_telnet = FALSE;
cfg->line_data.first_char = TRUE;
if (cfg->line_data.valid_conn == TRUE) {
ip_disconnect(cfg->line_data.fd);
cfg->line_data.valid_conn = FALSE;
}
}
return 0;
}

14
src/line.h Normal file
View File

@ -0,0 +1,14 @@
#ifndef LINE_H
#define LINE_H 1
int line_init(void);
int line_init_conn(modem_config *cfg);
int line_init_config(modem_config *cfg);
int line_read(modem_config *cfg, char *data, int len);
int line_write(modem_config *cfg, char *data, int len);
int line_listen(modem_config *cfg);
int line_off_hook(modem_config *cfg);
int line_connect(modem_config *cfg);
int line_disconnect(modem_config *cfg);
#endif

697
src/modem_core.c Normal file
View File

@ -0,0 +1,697 @@
#include <unistd.h>
#include <stdlib.h> // for atoi
#include "getcmd.h"
#include "debug.h"
#include "modem_core.h"
char *mdm_responses[37];
int mdm_init()
{
mdm_responses[MDM_RESP_OK] = "OK";
mdm_responses[MDM_RESP_RING] = "RING";
mdm_responses[MDM_RESP_ERROR] = "ERROR";
mdm_responses[MDM_RESP_CONNECT] = "CONNECT";
mdm_responses[MDM_RESP_NO_CARRIER] = "NO CARRIER";
mdm_responses[MDM_RESP_CONNECT_1200] = "CONNECT 1200";
mdm_responses[MDM_RESP_NO_DIALTONE] = "NO DIALTONE";
mdm_responses[MDM_RESP_BUSY] = "BUSY";
mdm_responses[MDM_RESP_NO_ANSWER] = "NO ANSWER";
mdm_responses[MDM_RESP_CONNECT_0600] = "CONNECT 0600";
mdm_responses[MDM_RESP_CONNECT_2400] = "CONNECT 2400";
mdm_responses[MDM_RESP_CONNECT_4800] = "CONNECT 4800";
mdm_responses[MDM_RESP_CONNECT_9600] = "CONNECT 9600";
mdm_responses[MDM_RESP_CONNECT_7200] = "CONNECT 7200";
mdm_responses[MDM_RESP_CONNECT_12000] = "CONNECT 12000";
mdm_responses[MDM_RESP_CONNECT_14400] = "CONNECT 14400";
mdm_responses[MDM_RESP_CONNECT_19200] = "CONNECT 19200";
mdm_responses[MDM_RESP_CONNECT_38400] = "CONNECT 38400";
mdm_responses[MDM_RESP_CONNECT_57600] = "CONNECT 57600";
mdm_responses[MDM_RESP_CONNECT_115200] = "CONNECT 115200";
mdm_responses[MDM_RESP_CONNECT_234000] = "CONNECT 230400";
return 0;
}
int get_connect_response(int speed, int level)
{
if (level == 0) {
return MDM_RESP_CONNECT;
}
switch (speed) {
case 115200:
return MDM_RESP_CONNECT_115200;
case 57600:
return MDM_RESP_CONNECT_57600;
case 38400:
return MDM_RESP_CONNECT_38400;
case 19200:
return MDM_RESP_CONNECT_19200;
case 9600:
return MDM_RESP_CONNECT_9600;
case 4800:
return MDM_RESP_CONNECT_4800;
case 2400:
return MDM_RESP_CONNECT_2400;
case 1200:
return MDM_RESP_CONNECT_1200;
case 600:
return MDM_RESP_CONNECT_0600;
}
return MDM_RESP_CONNECT;
}
void mdm_init_config(modem_config *cfg)
{
int i = 0;
cfg->send_responses = TRUE;
cfg->connect_response = 0;
cfg->response_code_level = 4;
cfg->text_responses = TRUE;
cfg->echo = TRUE;
cfg->cmd_mode = TRUE;
cfg->conn_type = MDM_CONN_NONE;
cfg->off_hook = FALSE;
cfg->line_ringing = FALSE;
cfg->cur_line_idx = 0;
for (i = 0; i < 100; i++) {
cfg->s[i] = 0;
}
cfg->s[2] = 43;
cfg->s[3] = 13;
cfg->s[4] = 10;
cfg->s[5] = 8;
cfg->s[6] = 2;
cfg->s[7] = 50;
cfg->s[8] = 2;
cfg->s[9] = 6;
cfg->s[10] = 14;
cfg->s[11] = 95;
cfg->s[12] = 50;
cfg->crlf[0] = cfg->s[3];
cfg->crlf[1] = cfg->s[4];
cfg->crlf[2] = 0;
cfg->dial_type = 0;
cfg->last_dial_type = 0;
cfg->disconnect_delay = 0;
cfg->pre_break_delay = FALSE;
cfg->break_len = 0;
cfg->memory_dial = FALSE;
cfg->dsr_active = FALSE;
cfg->dsr_on = TRUE;
cfg->dcd_on = FALSE;
cfg->found_a = FALSE;
cfg->cmd_started = FALSE;
cfg->allow_transmit = TRUE;
cfg->invert_dsr = FALSE;
cfg->invert_dcd = FALSE;
cfg->config0[0] = '\0';
cfg->config1[0] = '\0';
dce_init_config(cfg);
sh_init_config(cfg);
line_init_config(cfg);
}
int get_new_cts_state(modem_config *cfg, int up)
{
return MDM_CL_CTS_HIGH;
}
int get_new_dsr_state(modem_config *cfg, int up)
{
if (cfg->dsr_on == TRUE)
return (cfg->invert_dsr == TRUE ? MDM_CL_DSR_LOW : MDM_CL_DSR_HIGH);
if ((up == TRUE && cfg->invert_dsr == FALSE)
|| (up == FALSE && cfg->invert_dsr == TRUE)
)
return MDM_CL_DSR_HIGH;
else
return MDM_CL_DSR_LOW;
}
int get_new_dcd_state(modem_config *cfg, int up)
{
if (cfg->dcd_on == TRUE)
return (cfg->invert_dcd == TRUE ? MDM_CL_DCD_LOW : MDM_CL_DCD_HIGH);
if ((up == TRUE && cfg->invert_dcd == FALSE)
|| (up == FALSE && cfg->invert_dcd == TRUE)
)
return MDM_CL_DCD_HIGH;
else
return MDM_CL_DCD_LOW;
}
int mdm_set_control_lines(modem_config *cfg)
{
int state = 0;
int up = (cfg->conn_type == MDM_CONN_NONE ? FALSE : TRUE);
state |= get_new_cts_state(cfg, up);
state |= get_new_dsr_state(cfg, up);
state |= get_new_dcd_state(cfg, up);
LOG(LOG_INFO, "Control Lines: DSR:%d DCD:%d CTS:%d",
((state & MDM_CL_DSR_HIGH) != 0 ? 1 : 0),
((state & MDM_CL_DCD_HIGH) != 0 ? 1 : 0), ((state & MDM_CL_CTS_HIGH) != 0 ? 1 : 0)
);
dce_set_control_lines(cfg, state);
return 0;
}
void mdm_write_char(modem_config *cfg, char data)
{
char str[2];
str[0] = data;
mdm_write(cfg, str, 1);
}
void mdm_write(modem_config *cfg, char data[], int len)
{
if (cfg->allow_transmit == TRUE) {
dce_write(cfg, data, len);
}
}
void mdm_write_parity(modem_config *cfg, char *data, int len)
{
unsigned char *buf;
int i;
unsigned int v;
if (cfg->allow_transmit == TRUE) {
buf = malloc(len);
memcpy(buf, data, len);
for (i = 0; i < len; i++) {
v = buf[i];
v = (((v * 0x0101010101010101ULL) & 0x8040201008040201ULL) % 0x1FF) & 1;
buf[i] |= (((cfg->parity >> !v)) & 1) << 7;
}
mdm_write(cfg, (char *) buf, len);
free(buf);
}
}
void mdm_send_response(int msg, modem_config *cfg)
{
char msgID[17];
LOG(LOG_DEBUG, "Sending %s response to modem", mdm_responses[msg]);
if (cfg->send_responses == TRUE) {
mdm_write_parity(cfg, cfg->crlf, 2);
if (cfg->text_responses == TRUE) {
LOG(LOG_ALL, "Sending text response");
mdm_write_parity(cfg, mdm_responses[msg], strlen(mdm_responses[msg]));
}
else {
LOG(LOG_ALL, "Sending numeric response");
sprintf(msgID, "%d", msg);
mdm_write_parity(cfg, msgID, strlen(msgID));
}
mdm_write_parity(cfg, cfg->crlf, 2);
}
}
int mdm_off_hook(modem_config *cfg)
{
LOG(LOG_INFO, "taking modem off hook");
cfg->off_hook = TRUE;
cfg->cmd_mode = FALSE;
line_off_hook(cfg);
return 0;
}
int mdm_answer(modem_config *cfg)
{
if (cfg->line_ringing == TRUE) {
cfg->conn_type = MDM_CONN_INCOMING;
mdm_off_hook(cfg);
mdm_set_control_lines(cfg);
mdm_print_speed(cfg);
}
else if (cfg->conn_type == MDM_CONN_INCOMING) {
// we are connected, just go off hook.
mdm_off_hook(cfg);
mdm_set_control_lines(cfg);
}
else {
mdm_disconnect(cfg);
}
return 0;
}
int mdm_print_speed(modem_config *cfg)
{
int speed;
switch (cfg->connect_response) {
case 2:
speed = cfg->dte_speed;
break;
default:
speed = cfg->dce_speed;
break;
}
mdm_send_response(get_connect_response(speed, cfg->response_code_level), cfg);
return 0;
}
int mdm_connect(modem_config *cfg)
{
mdm_off_hook(cfg);
if (cfg->conn_type == MDM_CONN_NONE) {
if (line_connect(cfg) == 0) {
cfg->conn_type = MDM_CONN_OUTGOING;
mdm_set_control_lines(cfg);
mdm_print_speed(cfg);
}
else {
cfg->conn_type = MDM_CONN_OUTGOING; // so disconnect will print NO CARRIER
mdm_disconnect(cfg);
}
}
return 0;
}
int mdm_listen(modem_config *cfg)
{
return line_listen(cfg);
}
int mdm_disconnect(modem_config *cfg)
{
int type;
LOG_ENTER();
LOG(LOG_INFO, "Disconnecting modem");
cfg->cmd_mode = TRUE;
cfg->off_hook = FALSE;
cfg->break_len = 0;
cfg->line_ringing = FALSE;
cfg->pre_break_delay = FALSE;
if (0 == line_disconnect(cfg)) {
type = cfg->conn_type;
cfg->conn_type = MDM_CONN_NONE;
mdm_set_control_lines(cfg);
if (type != MDM_CONN_NONE) {
mdm_send_response(MDM_RESP_NO_CARRIER, cfg);
usleep(cfg->disconnect_delay * 1000);
}
cfg->rings = 0;
mdm_listen(cfg);
}
else {
// line still connected.
}
LOG_EXIT();
return 0;
}
int mdm_parse_cmd(modem_config *cfg)
{
int done = FALSE;
int index = 0;
int num = 0;
int start = 0;
int end = 0;
int cmd = AT_CMD_NONE;
char *command = cfg->cur_line;
char tmp[256];
LOG_ENTER();
LOG(LOG_DEBUG, "Evaluating AT%s", command);
while (TRUE != done) {
if (cmd != AT_CMD_ERR) {
cmd = getcmd(command, &index, &num, &start, &end);
LOG(LOG_DEBUG, "Command: %c (%d), Flags: %d, index=%d, num=%d, data=%d-%d",
(cmd > -1 ? cmd & 0xff : ' '), cmd, cmd >> 8, index, num, start, end);
}
switch (cmd) {
case AT_CMD_ERR:
mdm_send_response(MDM_RESP_ERROR, cfg);
done = TRUE;
break;
case AT_CMD_END:
if (cfg->cmd_mode == TRUE)
mdm_send_response(MDM_RESP_OK, cfg);
done = TRUE;
break;
case AT_CMD_NONE:
done = TRUE;
break;
case 'O':
case 'A':
mdm_answer(cfg);
cmd = AT_CMD_END;
done = TRUE;
break;
case 'B': // 212A versus V.22 connection
if (num > 1) {
cmd = AT_CMD_ERR;
}
else {
//cfg->connect_1200=num;
}
break;
case 'D':
if (end > start) {
strncpy(cfg->dialno, command + start, end - start);
cfg->dialno[end - start] = '\0';
cfg->dial_type = (char) num;
cfg->last_dial_type = (char) num;
strncpy(cfg->last_dialno, command + start, end - start);
cfg->last_dialno[end - start] = '\0';
cfg->memory_dial = FALSE;
}
else if (num == 'L') {
strncpy(cfg->dialno, cfg->last_dialno, strlen(cfg->last_dialno));
cfg->dial_type = cfg->dial_type;
cfg->memory_dial = TRUE;
mdm_write(cfg, cfg->crlf, 2);
mdm_write(cfg, cfg->dialno, strlen(cfg->dialno));
}
else {
cfg->dialno[0] = 0;
cfg->last_dialno[0] = 0;
cfg->dial_type = 0;
cfg->last_dial_type = 0;
}
if (strlen(cfg->dialno) > 0) {
mdm_connect(cfg);
}
else {
mdm_off_hook(cfg);
}
done = TRUE;
break;
case 'E': // still need to define #2
if (num == 0)
cfg->echo = FALSE;
else if (num == 1)
cfg->echo = TRUE;
else {
cmd = AT_CMD_ERR;
}
break;
case 'H':
if (num == 0) {
mdm_disconnect(cfg);
}
else if (num == 1) {
mdm_answer(cfg);
}
else
cmd = AT_CMD_ERR;
break;
case 'I': // Information.
break;
case 'L': // Speaker volume
if (num < 1 || num > 3)
cmd = AT_CMD_ERR;
else {
//cfg->volume=num;
}
break;
case 'M': // speaker settings
if (num > 3)
cmd = AT_CMD_ERR;
else {
//cfg->speaker_setting=num;
}
break;
case 'N': // automode negotiate
if (num > 1)
cmd = AT_CMD_ERR;
else {
//cfg->auto_mode=num;
}
break;
case 'P': // defaut to pulse dialing
//cfg->default_dial_type=MDM_DT_PULSE;
break;
case 'Q': // still need to define #2
if (num == 0)
cfg->send_responses = TRUE;
else if (num == 1)
cfg->send_responses = FALSE;
else if (num == 2) // this should be yes orig/no answer.
cfg->send_responses = TRUE;
else {
cmd = AT_CMD_ERR;
}
break;
case 'S':
strncpy(tmp, command + start, end - start);
tmp[end - start] = '\0';
cfg->s[num] = atoi(tmp);
switch (num) {
case 3:
cfg->crlf[0] = cfg->s[3];
break;
case 4:
cfg->crlf[1] = cfg->s[4];
break;
}
break;
case AT_CMD_FLAG_QUERY | 'S':
sprintf(tmp, "%s%3.3d", cfg->crlf, cfg->s[num]);
mdm_write(cfg, tmp, strlen(tmp));
break;
case 'T': // defaut to tone dialing
//cfg->default_dial_type=MDM_DT_TONE;
break;
case 'V': // done
if (num == 0)
cfg->text_responses = FALSE;
else if (num == 1)
cfg->text_responses = TRUE;
else {
cmd = AT_CMD_ERR;
}
break;
case 'W':
if (num > -1 && num < 3)
cfg->connect_response = num;
else
cmd = AT_CMD_ERR;
break;
case 'X':
if (num > -1 && num < 5)
cfg->response_code_level = num;
else
cmd = AT_CMD_ERR;
break;
case 'Y': // long space disconnect.
if (num > 1)
cmd = AT_CMD_ERR;
else {
//cfg->long_disconnect=num;
}
break;
case 'Z': // long space disconnect.
if (num > 1)
cmd = AT_CMD_ERR;
else {
// set config0 to cur_line and go.
}
break;
case AT_CMD_FLAG_EXT + 'C':
switch (num) {
case 0:
cfg->dcd_on = TRUE;
mdm_set_control_lines(cfg);
break;
case 1:
cfg->dcd_on = FALSE;
mdm_set_control_lines(cfg);
break;
default:
cmd = AT_CMD_ERR;
break;
}
break;
case AT_CMD_FLAG_EXT + 'K':
// flow control.
switch (num) {
case 0:
dce_set_flow_control(cfg, 0);
break;
case 3:
dce_set_flow_control(cfg, MDM_FC_RTS);
break;
case 4:
dce_set_flow_control(cfg, MDM_FC_XON);
break;
case 5:
dce_set_flow_control(cfg, MDM_FC_XON);
// need to add passthrough.. Not sure how.
break;
case 6:
dce_set_flow_control(cfg, MDM_FC_XON | MDM_FC_RTS);
break;
default:
cmd = AT_CMD_ERR;
break;
}
break;
default:
break;
}
}
cfg->cur_line_idx = 0;
return cmd;
}
int mdm_handle_char(modem_config *cfg, char ch)
{
int parbit = ch & 0x80;
ch &= 0x7f; // ignore parity
if (cfg->echo == TRUE)
mdm_write_char(cfg, (ch | parbit));
if (cfg->cmd_started == TRUE) {
if (ch == (char) (cfg->s[5])) {
if (cfg->cur_line_idx == 0 && cfg->echo == TRUE) {
mdm_write_char(cfg, 'T');
}
else {
cfg->cur_line_idx--;
}
}
else if (ch == (char) (cfg->s[3])) {
// we have a line, process.
cfg->cur_line[cfg->cur_line_idx] = 0;
strncpy(cfg->last_cmd, cfg->cur_line, sizeof(cfg->last_cmd) - 1);
mdm_parse_cmd(cfg);
cfg->found_a = FALSE;
cfg->cmd_started = FALSE;
}
else {
cfg->cur_line[cfg->cur_line_idx++ % sizeof(cfg->cur_line)] = ch;
}
}
else if (cfg->found_a == TRUE) {
if (ch == 't' || ch == 'T') {
cfg->cmd_started = TRUE;
LOG(LOG_ALL, "'T' parsed in serial stream, switching to command parse mode");
cfg->parity |= parbit >> 6;
}
else if (ch == '/') {
LOG(LOG_ALL, "'/' parsed in the serial stream, replaying last command");
cfg->cur_line_idx = strlen(cfg->last_cmd);
strncpy(cfg->cur_line, cfg->last_cmd, cfg->cur_line_idx);
mdm_parse_cmd(cfg);
cfg->cmd_started = FALSE;
cfg->parity |= parbit >> 6;
}
else if (ch != 'a' && ch != 'A') {
cfg->found_a = FALSE;
}
}
else if (ch == 'a' || ch == 'A') {
LOG(LOG_ALL, "'A' parsed in serial stream");
cfg->found_a = TRUE;
cfg->parity = parbit >> 7;
}
return 0;
}
int mdm_clear_break(modem_config *cfg)
{
cfg->break_len = 0;
cfg->pre_break_delay = FALSE;
return 0;
}
int mdm_parse_data(modem_config *cfg, char *data, int len)
{
int i;
if (cfg->cmd_mode == TRUE) {
for (i = 0; i < len; i++) {
mdm_handle_char(cfg, data[i]);
}
}
else {
line_write(cfg, data, len);
if (cfg->pre_break_delay == TRUE) {
for (i = 0; i < len; i++) {
if (data[i] == (char) cfg->s[2]) {
LOG(LOG_DEBUG, "Break character received");
cfg->break_len++;
if (cfg->break_len > 3) { // more than 3, considered invalid
cfg->pre_break_delay = FALSE;
cfg->break_len = 0;
}
}
else {
LOG(LOG_ALL, "Found non-break character, cancelling break");
// chars past +++
mdm_clear_break(cfg);
}
}
}
}
return 0;
}
int mdm_handle_timeout(modem_config *cfg)
{
if (cfg->pre_break_delay == TRUE && cfg->break_len == 3) {
// pre and post break.
LOG(LOG_INFO, "Break condition detected");
cfg->cmd_mode = TRUE;
mdm_send_response(MDM_RESP_OK, cfg);
mdm_clear_break(cfg);
}
else if (cfg->pre_break_delay == FALSE) {
// pre break wait over.
LOG(LOG_DEBUG, "Initial Break Delay detected");
cfg->pre_break_delay = TRUE;
}
else if (cfg->pre_break_delay == TRUE && cfg->break_len > 0) {
LOG(LOG_ALL, "Inter-break-char delay time exceeded");
mdm_clear_break(cfg);
}
else if (cfg->s[30] != 0) {
// timeout...
LOG(LOG_INFO, "DTE communication inactivity timeout");
mdm_disconnect(cfg);
}
return 0;
}
int mdm_send_ring(modem_config *cfg)
{
LOG(LOG_DEBUG, "Sending 'RING' to modem");
cfg->line_ringing = TRUE;
mdm_send_response(MDM_RESP_RING, cfg);
cfg->rings++;
LOG(LOG_ALL, "Sent #%d ring", cfg->rings);
if (cfg->cmd_mode == FALSE || (cfg->s[0] != 0 && cfg->rings >= cfg->s[0])) {
mdm_answer(cfg);
}
return 0;
}

163
src/modem_core.h Normal file
View File

@ -0,0 +1,163 @@
#ifndef MODEM_CORE_H
#define MODEM_CORE_H 1
#define MDM_RESP_OK 0
#define MDM_RESP_CONNECT 1
#define MDM_RESP_RING 2
#define MDM_RESP_NO_CARRIER 3
#define MDM_RESP_ERROR 4
#define MDM_RESP_CONNECT_1200 5
#define MDM_RESP_NO_DIALTONE 6
#define MDM_RESP_BUSY 7
#define MDM_RESP_NO_ANSWER 8
#define MDM_RESP_CONNECT_0600 9
#define MDM_RESP_CONNECT_2400 10
#define MDM_RESP_CONNECT_4800 11
#define MDM_RESP_CONNECT_9600 12
#define MDM_RESP_CONNECT_7200 13
#define MDM_RESP_CONNECT_12000 14
#define MDM_RESP_CONNECT_14400 15
#define MDM_RESP_CONNECT_19200 16
#define MDM_RESP_CONNECT_38400 17
#define MDM_RESP_CONNECT_57600 18
#define MDM_RESP_CONNECT_115200 19
#define MDM_RESP_CONNECT_234000 20
#define MDM_CL_DSR_LOW 0
#define MDM_CL_DSR_HIGH 1
#define MDM_CL_DCD_LOW 0
#define MDM_CL_DCD_HIGH 2
#define MDM_CL_CTS_LOW 0
#define MDM_CL_CTS_HIGH 4
#define MDM_CL_DTR_LOW 0
#define MDM_CL_DTR_HIGH 8
#define MDM_FC_RTS 1
#define MDM_FC_XON 2
#define MDM_CONN_NONE 0
#define MDM_CONN_OUTGOING 1
#define MDM_CONN_INCOMING 2
#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif
#include "nvt.h"
#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#endif
typedef struct line_config {
int valid_conn;
int fd;
int sfd;
int is_telnet;
int first_char;
nvt_vars nvt_data;
} line_config;
typedef struct x_config {
int mp[2][2];
int cp[2][2];
int wp[2][2];
char no_answer[256];
char local_connect[256];
char remote_connect[256];
char local_answer[256];
char remote_answer[256];
char inactive[256];
unsigned int direct_conn;
char direct_conn_num[256];
} x_config;
typedef struct dce_config {
int is_ip232;
char tty[256];
int first_char;
int fd;
int dp[2][2];
int sSocket;
int ip232_is_connected;
int ip232_dtr;
int ip232_dcd;
int ip232_iac;
} dce_config;
typedef struct modem_config {
// master configuration information
// need to eventually change these
dce_config dce_data;
line_config line_data;
x_config data;
char config0[1024];
char config1[1024];
int dce_speed;
int dte_speed;
int conn_type;
int line_ringing;
int off_hook;
int dsr_active;
int dsr_on;
int dcd_on;
int invert_dsr;
int invert_dcd;
int allow_transmit;
int rings;
// command information
int pre_break_delay;
int found_a;
int cmd_started;
int cmd_mode;
char last_cmd[1024];
char cur_line[1024];
int cur_line_idx;
// dailing information
char dialno[256];
char last_dialno[256];
char dial_type;
char last_dial_type;
int memory_dial;
// modem config
int connect_response;
int response_code_level;
int send_responses;
int text_responses;
int echo;
int s[100];
int break_len;
int disconnect_delay;
char crlf[3];
int parity;
} modem_config;
int mdm_init(void);
void mdm_init_config(modem_config *cfg);
int get_new_cts_state(modem_config *cfg, int up);
int get_new_dsr_state(modem_config *cfg, int up);
int get_new_dcd_state(modem_config *cfg, int up);
int mdm_set_control_lines(modem_config *cfg);
void mdm_write_char(modem_config *cfg, char data);
void mdm_write(modem_config *cfg, char data[], int len);
void mdm_send_response(int msg, modem_config *cfg);
int mdm_off_hook(modem_config *cfg);
int mdm_answer(modem_config *cfg);
int mdm_print_speed(modem_config *cfg);
int mdm_connect(modem_config *cfg);
int mdm_listen(modem_config *cfg);
int mdm_disconnect(modem_config *cfg);
int mdm_parse_cmd(modem_config *cfg);
int mdm_handle_char(modem_config *cfg, char ch);
int mdm_clear_break(modem_config *cfg);
int mdm_parse_data(modem_config *cfg, char *data, int len);
int mdm_handle_timeout(modem_config *cfg);
int mdm_send_ring(modem_config *cfg);
#include "line.h"
#include "shared.h"
#include "dce.h"
#endif

165
src/nvt.c Normal file
View File

@ -0,0 +1,165 @@
#include <string.h>
#include "debug.h"
#include "ip.h"
#include "nvt.h"
int nvt_init_config(nvt_vars *vars)
{
int i;
vars->binary_xmit = FALSE;
vars->binary_recv = FALSE;
for (i = 0; i < 256; i++)
vars->term[i] = 0;
return 0;
}
char get_nvt_cmd_response(char action, char type)
{
char rc = 0;
if (type == TRUE) {
switch (action) {
case NVT_DO:
rc = NVT_WILL_RESP;
break;
case NVT_DONT:
rc = NVT_WONT_RESP;
break;
case NVT_WILL:
rc = NVT_DO_RESP;
break;
case NVT_WONT:
rc = NVT_DONT_RESP;
break;
}
}
else {
switch (action) {
case NVT_DO:
case NVT_DONT:
rc = NVT_WONT_RESP;
break;
case NVT_WILL:
case NVT_WONT:
rc = NVT_DONT_RESP;
break;
}
}
return rc;
}
int parse_nvt_subcommand(int fd, nvt_vars *vars, char *data, int len)
{
// overflow issue, again...
int opt = data[2];
char resp[100];
char *response = resp + 3;
int resp_len = 0;
int response_len = 0;
char tty_type[] = "VT100";
int rc;
int slen = 0;
for (rc = 2; rc < len - 1; rc++) {
if (NVT_IAC == data[rc])
if (NVT_SE == data[rc + 1]) {
rc += 2;
break;
}
}
if (rc > 5 && (NVT_SB_SEND == data[4])) {
switch (opt) {
case NVT_OPT_TERMINAL_TYPE:
case NVT_OPT_X_DISPLAY_LOCATION: // should not have to have these
case NVT_OPT_ENVIRON: // but telnet seems to expect.
case NVT_OPT_NEW_ENVIRON: // them.
case NVT_OPT_TERMINAL_SPEED:
response[response_len++] = NVT_SB_IS;
switch (opt) {
case NVT_OPT_TERMINAL_TYPE:
slen = strlen(tty_type);
strncpy(response + response_len, tty_type, slen);
response_len += slen;
break;
}
break;
}
}
if (response_len) {
resp[resp_len++] = NVT_IAC;
resp[resp_len++] = NVT_SB;
resp[resp_len++] = opt;
resp_len += response_len;
resp[resp_len++] = NVT_IAC;
resp[resp_len++] = NVT_SE;
ip_write(fd, resp, resp_len);
}
return rc;
}
int send_nvt_command(int fd, nvt_vars *vars, char action, int opt)
{
char cmd[3];
cmd[0] = NVT_IAC;
cmd[1] = action;
cmd[2] = opt;
ip_write(fd, cmd, 3);
vars->term[opt] = action;
return 0;
}
int parse_nvt_command(int fd, nvt_vars *vars, char action, int opt)
{
char resp[3];
resp[0] = NVT_IAC;
resp[2] = opt;
switch (opt) {
case NVT_OPT_TRANSMIT_BINARY:
switch (action) {
case NVT_DO:
LOG(LOG_INFO, "Enabling telnet binary xmit");
vars->binary_xmit = TRUE;
break;
case NVT_DONT:
LOG(LOG_INFO, "Disabling telnet binary xmit");
vars->binary_xmit = FALSE;
break;
case NVT_WILL:
LOG(LOG_INFO, "Enabling telnet binary recv");
vars->binary_recv = TRUE;
break;
case NVT_WONT:
LOG(LOG_INFO, "Disabling telnet binary recv");
vars->binary_recv = FALSE;
break;
}
// fall through to get response
case NVT_OPT_NAWS:
case NVT_OPT_TERMINAL_TYPE:
case NVT_OPT_SUPPRESS_GO_AHEAD:
case NVT_OPT_ECHO:
case NVT_OPT_X_DISPLAY_LOCATION: // should not have to have these
case NVT_OPT_ENVIRON: // but telnet seems to expect.
case NVT_OPT_NEW_ENVIRON: // them.
case NVT_OPT_TERMINAL_SPEED:
resp[1] = get_nvt_cmd_response(action, TRUE);
break;
default:
resp[1] = get_nvt_cmd_response(action, FALSE);
break;
}
ip_write(fd, resp, 3);
return 0;
}

54
src/nvt.h Normal file
View File

@ -0,0 +1,54 @@
#ifndef NVT_H
#define NVT_H 1
#define NVT_SE 240
#define NVT_NOP 241
#define NVT_DM 242
#define NVT_SB 250
#define NVT_WILL 251
#define NVT_WONT 252
#define NVT_DO 253
#define NVT_DONT 254
#define NVT_IAC 255
#define NVT_WILL_RESP 251
#define NVT_WONT_RESP 252
#define NVT_DO_RESP 253
#define NVT_DONT_RESP 254
#define NVT_OPT_TRANSMIT_BINARY 0
#define NVT_OPT_ECHO 1
#define NVT_OPT_SUPPRESS_GO_AHEAD 3
#define NVT_OPT_STATUS 5
#define NVT_OPT_RCTE 7
#define NVT_OPT_TIMING_MARK 6
#define NVT_OPT_NAOCRD 10
#define NVT_OPT_TERMINAL_TYPE 24
#define NVT_OPT_NAWS 31
#define NVT_OPT_TERMINAL_SPEED 32
#define NVT_OPT_LINEMODE 34
#define NVT_OPT_X_DISPLAY_LOCATION 35
#define NVT_OPT_ENVIRON 36
#define NVT_OPT_NEW_ENVIRON 39
#define NVT_SB_IS 0
#define NVT_SB_SEND 1
#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif
typedef struct nvt_vars {
int binary_xmit;
int binary_recv;
char term[256];
} nvt_vars;
char get_nvt_cmd_response(char action, char type);
int parse_nvt_subcommand(int fd, nvt_vars *vars, char *data, int len);
int parse_nvt_command(int fd, nvt_vars *vars, char action, int opt);
int nvt_init_config(nvt_vars *vars);
int send_nvt_command(int fd, nvt_vars *vars, char action, int opt);
#endif

46
src/phone_book.c Normal file
View File

@ -0,0 +1,46 @@
#include <stdio.h>
#include <string.h>
#include "phone_book.h"
#include "debug.h"
#define PBSIZE 100
char phone_book[PBSIZE][2][128];
int size = 0;
int pb_init()
{
return 0;
}
int pb_add(char *from, char *to)
{
LOG_ENTER();
if (size < PBSIZE && from != NULL && to != NULL && strlen(from) > 0 && strlen(to) > 0) {
// should really trim spaces.
strncpy(phone_book[size][0], from, sizeof(phone_book[size][0]));
strncpy(phone_book[size][1], to, sizeof(phone_book[size][1]));
size++;
LOG_EXIT();
return 0;
}
LOG_EXIT();
return -1;
}
char *pb_search(char *number)
{
int i = 0;
LOG_ENTER();
for (i = 0; i < size; i++) {
if (strcmp(phone_book[i][0], number) == 0) {
LOG(LOG_INFO, "Found a match for '%s': '%s'", number, phone_book[i][1]);
strcpy(number, phone_book[i][1]);
break;
}
}
LOG_EXIT();
return number;
}

3
src/phone_book.h Normal file
View File

@ -0,0 +1,3 @@
int pb_init(void);
int pb_add(char *from, char *to);
char *pb_search(char *number);

188
src/serial.c Normal file
View File

@ -0,0 +1,188 @@
#include <sys/file.h>
#include <unistd.h>
#include <termios.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include "debug.h"
#include "serial.h"
int ser_get_bps_const(int speed)
{
int bps_rate = 0;
LOG_ENTER();
LOG(LOG_DEBUG, "Checking speed: %d", speed);
switch (speed) {
case 115200:
bps_rate = B115200;
break;
case 57600:
bps_rate = B57600;
break;
case 38400:
bps_rate = B38400;
break;
case 19200:
bps_rate = B19200;
break;
case 9600:
bps_rate = B9600;
break;
case 4800:
bps_rate = B4800;
break;
case 2400:
bps_rate = B2400;
break;
case 1200:
bps_rate = B1200;
break;
case 600:
bps_rate = B600;
break;
case 300:
bps_rate = B300;
break;
case 150:
bps_rate = B150;
break;
case 134:
bps_rate = B134;
break;
case 110:
bps_rate = B110;
break;
case 75:
bps_rate = B75;
break;
case 50:
bps_rate = B50;
break;
case 0:
bps_rate = B0;
break;
default:
ELOG(LOG_FATAL, "Unknown baud rate");
bps_rate = -1;
}
LOG_EXIT();
return bps_rate;
}
int ser_init_conn(char *tty, int speed)
{
int fd = -1;
struct termios tio;
int bps_rate = 0;
LOG_ENTER();
bps_rate = ser_get_bps_const(speed);
if (bps_rate > -1) {
/* open the device to be non-blocking (read will return immediatly) */
LOG(LOG_INFO, "Opening serial device");
fd = open(tty, O_RDWR | O_NOCTTY);
if (fd < 0) {
ELOG(LOG_FATAL, "TTY %s could not be opened", tty);
}
else {
LOG(LOG_INFO, "Opened serial device %s at speed %d as fd %d", tty, speed, fd);
/* Make the file descriptor asynchronous (the manual page says only
O_APPEND and O_NONBLOCK, will work with F_SETFL...) */
fcntl(fd, F_SETFL, FASYNC);
tio.c_cflag = bps_rate | CS8 | CLOCAL | CREAD | CRTSCTS;
tio.c_iflag = (IGNBRK);
tio.c_oflag = 0;
tio.c_lflag = 0;
tio.c_cc[VMIN] = 1;
tio.c_cc[VTIME] = 0;
tcflush(fd, TCIFLUSH);
cfsetispeed(&tio, bps_rate);
cfsetospeed(&tio, bps_rate);
tcsetattr(fd, TCSANOW, &tio);
LOG(LOG_INFO, "serial device configured");
}
}
LOG_EXIT();
return fd;
}
int ser_set_flow_control(int fd, int status)
{
struct termios tio;
if (0 != tcgetattr(fd, &tio)) {
ELOG(LOG_FATAL, "Could not get serial port attributes");
return -1;
}
// turn all off.
tio.c_cflag &= ~(IXON | IXOFF | CRTSCTS);
tio.c_cflag |= status;
if (0 != tcsetattr(fd, TCSANOW, &tio)) {
ELOG(LOG_FATAL, "Could not set serial port attributes");
return -1;
}
return 0;
}
int ser_get_control_lines(int fd)
{
int status;
if (0 > ioctl(fd, TIOCMGET, &status)) {
ELOG(LOG_FATAL, "Could not obtain serial port status");
return -1;
}
return status;
}
int ser_set_control_lines(int fd, int state)
{
unsigned int status;
if (0 > (status = ser_get_control_lines(fd))) {
return status;
}
status &= ~(TIOCM_RTS | TIOCM_DTR);
status |= state;
if (0 > ioctl(fd, TIOCMSET, &status)) {
#ifndef WIN32
ELOG(LOG_FATAL, "Could not set serial port status");
return -1;
#else
ELOG(LOG_WARN, "Could not set serial port status, CYGWIN bug?");
#endif
}
return 0;
}
int ser_write(int fd, char *data, int len)
{
log_trace(TRACE_MODEM_OUT, data, len);
return write(fd, data, len);
}
int ser_read(int fd, char *data, int len)
{
int res;
res = read(fd, data, len);
log_trace(TRACE_MODEM_IN, data, res);
return res;
}

17
src/serial.h Normal file
View File

@ -0,0 +1,17 @@
#ifndef SERIAL_H
#define SERIAL_H 1
#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif
int ser_get_bps_const(int speed);
int ser_init_conn(char *tty, int speed);
int ser_set_flow_control(int fd, int status);
int ser_get_control_lines(int fd);
int ser_set_control_lines(int fd, int state);
int ser_write(int fd, char *data, int len);
int ser_read(int fd, char *data, int len);
#endif

14
src/shared.c Normal file
View File

@ -0,0 +1,14 @@
#include "modem_core.h"
int sh_init_config(modem_config *cfg)
{
cfg->data.local_connect[0] = 0;
cfg->data.remote_connect[0] = 0;
cfg->data.local_answer[0] = 0;
cfg->data.remote_answer[0] = 0;
cfg->data.no_answer[0] = 0;
cfg->data.inactive[0] = 0;
cfg->data.direct_conn = FALSE;
cfg->data.direct_conn_num[0] = 0;
return 0;
}

6
src/shared.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef SHARED_H
#define SHARED_H 1
int sh_init_config(modem_config *cfg);
#endif

146
src/tcpser.c Normal file
View File

@ -0,0 +1,146 @@
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/param.h>
#include <pthread.h>
#include "init.h"
#include "ip.h"
#include "modem_core.h"
#include "bridge.h"
#include "phone_book.h"
#include "util.h"
#include "debug.h"
const char MDM_BUSY[] = "BUSY\n";
int main(int argc, char *argv[])
{
modem_config cfg[64];
int modem_count;
int port = 0;
char all_busy[255];
pthread_t thread_id;
int i;
int rc;
int sSocket = 0;
fd_set readfs;
int max_fd = 0;
int accept_pending = FALSE;
int res = 0;
char buf[255];
int cSocket;
log_init();
LOG_ENTER();
log_set_level(LOG_FATAL);
mdm_init();
pb_init();
signal(SIGIO, SIG_IGN); /* Some Linux variant term on SIGIO by default */
modem_count = init(argc, argv, cfg, 64, &port, all_busy, sizeof(all_busy));
sSocket = ip_init_server_conn(port);
for (i = 0; i < modem_count; i++) {
if (-1 == pipe(cfg[i].data.mp[0])) {
ELOG(LOG_FATAL, "Bridge task incoming IPC pipe could not be created");
exit(-1);
}
if (-1 == pipe(cfg[i].data.mp[1])) {
ELOG(LOG_FATAL, "Bridge task outgoing IPC pipe could not be created");
exit(-1);
}
if (dce_init_conn(&cfg[i]) < 0) {
LOG(LOG_FATAL, "Could not open serial port %s", cfg->dce_data.tty);
exit(-1);
}
cfg[i].line_data.sfd = sSocket;
rc = pthread_create(&thread_id, NULL, *run_bridge, (void *) &cfg[i]);
if (rc < 0) {
ELOG(LOG_FATAL, "IP thread could not be started");
exit(-1);
}
}
for (;;) {
FD_ZERO(&readfs);
max_fd = 0;
for (i = 0; i < modem_count; i++) {
FD_SET(cfg[i].data.mp[0][0], &readfs);
max_fd = MAX(max_fd, cfg[i].data.mp[0][0]);
}
if (accept_pending == FALSE) {
max_fd = MAX(max_fd, sSocket);
FD_SET(sSocket, &readfs);
}
LOG(LOG_ALL, "Waiting for incoming connections and/or indicators");
select(max_fd + 1, &readfs, NULL, NULL, NULL);
for (i = 0; i < modem_count; i++) {
if (FD_ISSET(cfg[i].data.mp[0][0], &readfs)) { // child pipe
res = read(cfg[i].data.mp[0][0], buf, sizeof(buf) - 1);
if (res > -1) {
buf[res] = 0;
LOG(LOG_DEBUG, "modem core #%d sent response '%c'", i, buf[0]);
accept_pending = FALSE;
}
}
}
if (FD_ISSET(sSocket, &readfs)) { // IP traffic
if (!accept_pending) {
LOG(LOG_DEBUG, "Incoming connection pending");
// first try for a modem that is listening.
for (i = 0; i < modem_count; i++) {
if (cfg[i].s[0] != 0 && cfg[i].off_hook == FALSE) {
// send signal to pipe saying pick up...
LOG(LOG_DEBUG, "Sending incoming connection to listening modem #%d", i);
writePipe(cfg[i].data.mp[1][1], MSG_ACCEPT);
accept_pending = TRUE;
break;
}
}
// now, send to any non-active modem.
for (i = 0; i < modem_count; i++) {
if (cfg[i].off_hook == FALSE) {
// send signal to pipe saying pick up...
LOG(LOG_DEBUG, "Sending incoming connection to non-connected modem #%d", i);
writePipe(cfg[i].data.mp[1][1], MSG_ACCEPT);
accept_pending = TRUE;
break;
}
}
if (i == modem_count) {
LOG(LOG_DEBUG, "No open modem to send to, send notice and close");
// no connections.., accept and print error
cSocket = ip_accept(sSocket);
if (cSocket > -1) {
if (strlen(all_busy) < 1) {
ip_write(cSocket, (char *) MDM_BUSY, strlen(MDM_BUSY));
}
else {
writeFile(all_busy, cSocket);
}
close(cSocket);
}
}
}
}
}
LOG_EXIT();
return rc;
}

35
src/util.c Normal file
View File

@ -0,0 +1,35 @@
#include <unistd.h>
#include <stdio.h>
#include "util.h"
int writePipe(int fd, char msg)
{
char tmp[3];
tmp[0] = msg;
tmp[1] = '\n';
tmp[2] = '\0';
//printf("Writing %c to pipe fd: %d\n",msg,fd);
return write(fd, tmp, 2);
}
int writeFile(char *name, int fd)
{
FILE *file;
char buf[255];
size_t len;
size_t size = 1;
size_t max = 255;
if (NULL != (file = fopen(name, "rb"))) {
while (0 < (len = fread(buf, size, max, file))) {
write(fd, buf, len);
}
fclose(file);
return 0;
}
return -1;
}

2
src/util.h Normal file
View File

@ -0,0 +1,2 @@
int writePipe(int fd, char msg);
int writeFile(char *name, int fd);