Skip to content
Snippets Groups Projects
Commit 3ec7fb77 authored by Rob Swindell's avatar Rob Swindell :speech_balloon:
Browse files

Synchronet Virtual DOS Modem for Windows

First commit.

Right now it just connects/accepts-connections using raw TCP. As demonstrated in YouTube video:
https://www.youtube.com/watch?v=fxp38Nde3fg
parent 07f57a86
No related branches found
No related tags found
No related merge requests found
/* Synchronet Virtual DOS Modem for Windows */
/****************************************************************************
* @format.tab-size 4 (Plain Text/Source Code File Header) *
* @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) *
* *
* Copyright Rob Swindell - http://www.synchro.net/copyright.html *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License *
* as published by the Free Software Foundation; either version 2 *
* of the License, or (at your option) any later version. *
* See the GNU General Public License for more details: gpl.txt or *
* http://www.fsf.org/copyleft/gpl.html *
* *
* For Synchronet coding style and modification guidelines, see *
* http://www.synchro.net/source.html *
* *
* Note: If this box doesn't appear square, then you need to fix your tabs. *
****************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#define WIN32_LEAN_AND_MEAN
#define NOGDI
#include <windows.h>
#include <process.h>
#include "genwrap.h"
#include "gen_defs.h"
#include "sockwrap.h"
#define TITLE "Synchronet Virtual DOS Modem for Windows"
#define VERSION "0.0"
SOCKET sock = INVALID_SOCKET;
SOCKET listening_sock = INVALID_SOCKET;
HANDLE hangup_event = INVALID_HANDLE_VALUE; // e.g. program drops DTR
HANDLE hungup_event = INVALID_HANDLE_VALUE; // e.g. ATH0
HANDLE carrier_event = INVALID_HANDLE_VALUE;
HANDLE rdslot = INVALID_HANDLE_VALUE;
HANDLE wrslot = INVALID_HANDLE_VALUE;
union xp_sockaddr listening_interface;
#define XTRN_IO_BUF_LEN 10000
#define RING_DELAY 6000 /* US standard is 6 seconds */
struct {
int node_num;
bool listen;
bool debug;
bool terminate_on_disconnect;
ulong data_rate;
enum {
ADDRESS_FAMILY_UNSPEC
,ADDRESS_FAMILY_INET
,ADDRESS_FAMILY_INET6
} address_family;
} cfg;
static void dprintf(const char *fmt, ...)
{
char buf[1024] = "SBBSVDM: ";
va_list argptr;
va_start(argptr,fmt);
size_t offset = strlen(buf);
_vsnprintf(buf + offset, sizeof(buf) - offset, fmt, argptr);
TERMINATE(buf);
va_end(argptr);
OutputDebugString(buf);
}
void usage(void)
{
fprintf(stderr, "usage:\n");
exit(EXIT_SUCCESS);
}
const char* supported_cmds = "ADEHIMOQSVXZ&";
const char* string_cmds = "D";
struct modem {
enum {
INIT
,A
,AT
} cmdstate;
char cr;
char lf;
char bs;
char esc;
bool echo_off;
bool numeric_mode;
bool offhook;
bool online; // false means "command mode"
bool ringing;
ulong ringcount;
ulong auto_answer;
ulong dial_wait;
ulong guard_time;
ulong esc_count;
ulong ext_results;
ulong quiet;
uint8_t buf[128];
size_t buflen;
};
void newcmd(struct modem* modem)
{
modem->cmdstate = INIT;
modem->buflen = 0;
}
void init(struct modem* modem)
{
memset(modem, 0, sizeof(*modem));
modem->cr = '\r';
modem->lf = '\n';
modem->bs = '\b';
modem->esc = '+';
modem->ext_results = 4;
modem->dial_wait = 60;
modem->guard_time = 50;
}
ulong guard_time(struct modem* modem)
{
return modem->guard_time * 20;
}
ulong count_esc(struct modem* modem, uint8_t* buf, size_t rd)
{
if(modem->esc < 0 || modem->esc > 127)
return 0;
ulong count = 0;
for(size_t i = 0; i < rd; i++) {
if(buf[i] == modem->esc)
count++;
}
return count;
}
// Basic Hayes modem responses
enum modem_response {
OK = 0,
CONNECT = 1,
RING = 2,
NO_CARRIER = 3,
ERROR = 4,
CONNECT_1200 = 5,
NO_DIAL_TONE = 6,
BUSY = 7,
NO_ANSWER = 8,
RESERVED1 = 9,
CONNECT_2400 = 10,
RESERVED2 = 11,
RESERVED3 = 12,
CONNECT_9600 = 13
};
const char* response_str[] = {
"OK",
"CONNECT",
"RING",
"NO CARRIER",
"ERROR",
"CONNECT 1200",
"NO DIAL TONE",
"BUSY",
"NO ANSWER",
"RESERVED1",
"CONNECT 2400",
"RESERVED2",
"RESERVED3",
"CONNECT 9600"
};
char* response(struct modem* modem, enum modem_response code)
{
static char str[128];
if(modem->quiet)
return "";
if(modem->numeric_mode)
sprintf(str, "%u%c", code, modem->cr);
else
sprintf(str, "%c%c%s%c%c", modem->cr, modem->lf, response_str[code], modem->cr, modem->lf);
return str;
}
char* ok(struct modem* modem)
{
return response(modem, OK);
}
char* error(struct modem* modem)
{
return response(modem, ERROR);
}
char* connect_result(struct modem* modem)
{
return response(modem, modem->ext_results ? CONNECT_9600 : CONNECT);
}
char* connected(struct modem* modem)
{
modem->online = true;
modem->ringing = false;
ResetEvent(hungup_event);
SetEvent(carrier_event);
return connect_result(modem);
}
void disconnect(struct modem* modem)
{
modem->online = false;
shutdown(sock, SD_SEND);
closesocket(sock);
sock = INVALID_SOCKET;
SetEvent(hungup_event);
SetEvent(carrier_event);
}
bool kbhit()
{
unsigned long waiting = 0;
if(!GetMailslotInfo(
rdslot, // mailslot handle
NULL, // address of maximum message size
NULL, // address of size of next message
&waiting, // address of number of messages
NULL // address of read time-out
))
return false;
return waiting != 0;
}
// Significant portions copies from syncterm/conn.c
char* dial(struct modem* modem, const char* number)
{
struct addrinfo hints;
struct addrinfo *res=NULL;
char host[128];
char* portnum = "23";
SAFECOPY(host, number);
char* p = strrchr(host, ':');
char* b = strrchr(host, ']');
if(p != NULL && p > b) {
portnum = p + 1;
*p = 0;
}
dprintf("Connecting to host '%s', port: %u", host, portnum);
memset(&hints, 0, sizeof(hints));
hints.ai_flags=PF_UNSPEC;
switch(cfg.address_family) {
case ADDRESS_FAMILY_INET:
hints.ai_family=PF_INET;
break;
case ADDRESS_FAMILY_INET6:
hints.ai_family=PF_INET6;
break;
case ADDRESS_FAMILY_UNSPEC:
default:
hints.ai_family=PF_UNSPEC;
break;
}
hints.ai_socktype=SOCK_STREAM;
hints.ai_protocol=IPPROTO_TCP;
hints.ai_flags=AI_NUMERICSERV;
#ifdef AI_ADDRCONFIG
hints.ai_flags|=AI_ADDRCONFIG;
#endif
dprintf("%s %d calling getaddrinfo", __FILE__, __LINE__);
int result = getaddrinfo(host, portnum, &hints, &res);
if(result != 0) {
dprintf("getaddrinfo(%s, %s) returned %d", host, portnum, result);
return response(modem, NO_ANSWER);
}
int nonblock;
struct addrinfo *cur;
for(cur=res; cur && sock == INVALID_SOCKET; cur=cur->ai_next) {
if(sock==INVALID_SOCKET) {
sock=socket(cur->ai_family, cur->ai_socktype, cur->ai_protocol);
if(sock==INVALID_SOCKET) {
dprintf("Error %ld creating socket", WSAGetLastError());
return response(modem, NO_DIAL_TONE);
}
/* Set to non-blocking for the connect */
nonblock=-1;
ioctlsocket(sock, FIONBIO, &nonblock);
}
dprintf("%s %d calling connect", __FILE__, __LINE__);
if(connect(sock, cur->ai_addr, cur->ai_addrlen)) {
switch(ERROR_VALUE) {
case EINPROGRESS:
case EINTR:
case EAGAIN:
case EWOULDBLOCK:
for(;sock!=INVALID_SOCKET;) {
if (socket_writable(sock, 1000)) {
if (socket_recvdone(sock, 0)) {
closesocket(sock);
sock=INVALID_SOCKET;
continue;
}
else {
goto connected;
}
}
else {
if (kbhit()) {
dprintf("%s %d kbhit", __FILE__, __LINE__);
closesocket(sock);
sock = INVALID_SOCKET;
return response(modem, NO_CARRIER);
}
}
}
connected:
break;
default:
closesocket(sock);
sock=INVALID_SOCKET;
continue;
}
}
}
if (sock == INVALID_SOCKET) {
dprintf("%s %d invalid hostname?", __FILE__, __LINE__);
return response(modem, NO_ANSWER);
}
freeaddrinfo(res);
res=NULL;
nonblock=0;
ioctlsocket(sock, FIONBIO, &nonblock);
if(socket_recvdone(sock, 0)) {
dprintf("%s %d socket_recvdone", __FILE__, __LINE__);
return response(modem, NO_CARRIER);
}
dprintf("%s %d connected!", __FILE__, __LINE__);
int keepalives = TRUE;
setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, (void*)&keepalives, sizeof(keepalives));
return connected(modem);
}
char* answer(struct modem* modem)
{
if(listening_sock == INVALID_SOCKET)
return response(modem, NO_DIAL_TONE);
fd_set fds = {0};
FD_SET(listening_sock, &fds);
struct timeval tv = { 0, 0 };
if(select(/* ignored: */0, &fds, NULL, NULL, &tv) != 1)
return response(modem, NO_CARRIER);
union xp_sockaddr addr;
socklen_t addrlen = sizeof(addr);
sock = accept(listening_sock, (SOCKADDR*)&addr, &addrlen);
if(sock == INVALID_SOCKET) {
dprintf("accept returned %d (errno=%ld)", sock, WSAGetLastError());
return response(modem, NO_CARRIER);
}
char tmp[256];
dprintf("Connection accepted from TCP port %hu at %s", inet_addrport(&addr), inet_addrtop(&addr, tmp, sizeof(tmp)));
return connected(modem);
}
char* atmodem_exec(struct modem* modem)
{
static char respbuf[128];
char* resp = ok(modem);
modem->buf[modem->buflen] = '\0';
for(char* p = modem->buf; *p != '\0';) {
char ch = toupper(*p);
p++;
if(strchr(supported_cmds, ch) == NULL)
return error(modem);
if(strchr(string_cmds, ch) == NULL) {
if(ch == '&') {
p++;
ch = toupper(*p); // unused
ulong val = strtoul(p, &p, 10); // unused
continue;
}
// Numeric argument commands
ulong val = strtoul(p, &p, 10);
switch(ch) {
case 'A':
return answer(modem);
case 'E':
modem->echo_off = !val;
break;
case 'H':
modem->offhook = val;
modem->ringing = false;
if(!modem->offhook) {
if(sock != INVALID_SOCKET) {
disconnect(modem);
}
}
break;
case 'I':
sprintf(respbuf, "\r\n" TITLE " v" VERSION " Copyright %s Rob Swindell\r\n", &__DATE__[7]);
return respbuf;
case 'O':
if(sock == INVALID_SOCKET)
return error(modem);
modem->online = true;
return connect_result(modem);
break;
case 'V':
modem->numeric_mode = !val;
resp = ok(modem); // Use the new verbal/numeric mode in response (ala USRobotics)
break;
case 'Q':
modem->quiet = val;
resp = ok(modem); // Use the new quiet/verbose mode in response (ala USRobotics)
break;
case 'S':
if(*p == '=') {
ulong sreg = val;
ulong val = strtoul(p + 1, &p, 10);
dprintf("S%lu = %lu", sreg, val);
switch(sreg) {
case 0:
if(val && listening_sock == INVALID_SOCKET)
return error(modem);
modem->auto_answer = val;
break;
case 1:
modem->ringcount = val;
break;
case 2:
modem->esc = (char)val;
break;
case 3:
modem->cr = (char)val;
break;
case 4:
modem->lf = (char)val;
break;
case 5:
modem->bs = (char)val;
break;
case 7:
modem->dial_wait = val;
break;
case 12:
modem->guard_time = val;
break;
}
} else if(*p == '?') {
switch(val) {
case 0:
val = modem->auto_answer;
break;
case 1:
val = modem->ringcount;
break;
case 2:
val = modem->esc;
break;
case 3:
val = modem->cr;
break;
case 4:
val = modem->lf;
break;
case 5:
val = modem->bs;
break;
case 7:
val = modem->dial_wait;
break;
case 12:
val = modem->guard_time;
break;
default:
val = 0;
break;
}
sprintf(respbuf, "%c%03lu%c%c%s", modem->lf, val, modem->cr, modem->lf, ok(modem));
return respbuf;
} else
return error(modem);
break;
case 'X':
modem->ext_results = val;
break;
case 'Z':
init(modem);
break;
}
} else { // string argument commands
switch(ch) {
case 'D':
if(sock != INVALID_SOCKET)
return error(modem);
if(*p == 'T' /* tone */|| *p == 'P' /* pulse */)
p++;
return dial(modem, p);
}
}
}
return resp;
}
char* atmodem_parsech(struct modem* modem, uint8_t ch)
{
switch(modem->cmdstate) {
case INIT:
if(toupper(ch) == 'A')
modem->cmdstate = A;
break;
case A:
if(toupper(ch) == 'T')
modem->cmdstate = AT;
else
newcmd(modem);
break;
case AT:
if(ch == modem->cr) {
char* retval = atmodem_exec(modem);
newcmd(modem);
return retval;
} else if(ch == modem->bs) {
if(modem->buflen)
modem->buflen--;
} else {
if(modem->buflen >= sizeof(modem->buf))
return error(modem);
if(ch != ' ')
modem->buf[modem->buflen++] = ch;
}
break;
}
return NULL;
}
char* atmodem_parse(struct modem* modem, uint8_t* buf, size_t len)
{
for(size_t i = 0; i < len; i++) {
char* resp = atmodem_parsech(modem, buf[i]);
if(resp != NULL)
return resp;
}
return NULL;
}
BOOL vdd_write(HANDLE* slot, uint8_t* buf, size_t buflen)
{
if(*slot == INVALID_HANDLE_VALUE) {
char path[MAX_PATH + 1];
sprintf(path, "\\\\.\\mailslot\\sbbsexec\\wr%d", cfg.node_num);
*slot = CreateFile(path
,GENERIC_WRITE
,FILE_SHARE_READ
,NULL
,OPEN_EXISTING
,FILE_ATTRIBUTE_NORMAL
,(HANDLE) NULL);
if(*slot == INVALID_HANDLE_VALUE) {
dprintf("!ERROR %u (%s) opening '%s'", GetLastError(), strerror(errno), path);
exit(1);
}
}
DWORD wr = 0;
BOOL result = WriteFile(*slot, buf, buflen, &wr, /* LPOVERLAPPED */NULL);
if(wr != buflen)
dprintf("WriteFile wrote %ld instead of %ld", wr, buflen);
return result;
}
BOOL vdd_writestr(HANDLE* slot, char* str)
{
return vdd_write(slot, str, strlen(str));
}
void listen_thread(void* arg)
{
struct modem* modem = (struct modem*)arg;
for(;;) {
fd_set fds = {0};
FD_SET(listening_sock, &fds);
struct timeval tv = { 1, 0 };
if(select(/* ignored: */0, &fds, NULL, NULL, &tv) == 1) {
if(sock != INVALID_SOCKET) { // In-use
SOCKADDR_IN addr;
socklen_t addrlen = sizeof(addr);
SOCKET newsock = accept(listening_sock, (SOCKADDR*)&addr, &addrlen);
if(newsock != INVALID_SOCKET) {
char* busy_notice = "\r\nSorry, not available right now\r\n";
send(newsock, busy_notice, strlen(busy_notice), /* flags: */0);
shutdown(newsock, SD_SEND);
closesocket(newsock);
}
continue;
}
if(!modem->offhook && !modem->online)
modem->ringing = true;
}
}
}
int main(int argc, char** argv)
{
int argn = 1;
char tmp[256];
char path[MAX_PATH + 1];
char fullmodemline[MAX_PATH + 1];
uint8_t buf[XTRN_IO_BUF_LEN];
size_t rx_buflen = sizeof(buf);
ULONGLONG rx_delay = 0;
WSADATA WSAData;
int result;
fprintf(stderr, TITLE " v" VERSION " Copyright %s Rob Swindell\n", &__DATE__[7]);
if((result = WSAStartup(MAKEWORD(1,1), &WSAData)) == 0)
dprintf("%s %s",WSAData.szDescription, WSAData.szSystemStatus);
else {
fprintf(stderr,"!WinSock startup ERROR %d", result);
return EXIT_FAILURE;
}
listening_interface.addr.sa_family = cfg.address_family;
for(; argn < argc; argn++) {
char* arg = argv[argn];
if(*arg != '-')
break;
while(*arg == '-')
arg++;
switch(*arg) {
case '6':
listening_interface.addr.sa_family = AF_INET6;
break;
case 'l':
cfg.listen = true;
arg++;
if(*arg != '\0') {
if(inet_ptoaddr(arg, &listening_interface, sizeof(listening_interface)) == NULL) {
fprintf(stderr, "!Error parsing network address: %s", arg);
return EXIT_FAILURE;
}
}
break;
case 'p':
inet_setaddrport(&listening_interface, atoi(arg + 1));
break;
case 'd':
cfg.debug = true;
break;
case 'h':
sock = strtoul(arg + 1, NULL, 10);
break;
case 'r':
cfg.data_rate = strtoul(arg + 1, NULL, 10);
break;
case 'b':
rx_buflen = min(strtoul(arg + 1, NULL, 10), sizeof(buf));
case 'R':
rx_delay = strtoul(arg + 1, NULL, 10);
break;
default:
usage();
break;
}
}
if(argn >= argc) {
usage();
}
struct modem modem = {0};
init(&modem);
if(cfg.listen) {
if(sock != INVALID_SOCKET)
listening_sock = sock;
else {
listening_sock = socket(PF_INET, SOCK_STREAM, IPPROTO_IP);
if(listening_sock == INVALID_SOCKET) {
fprintf(stderr, "Error %ld creating socket\n", WSAGetLastError());
return EXIT_FAILURE;
}
}
result = bind(listening_sock, &listening_interface.addr, xp_sockaddr_len(&listening_interface));
if(result != 0) {
fprintf(stderr, "Error %d binding socket\n", WSAGetLastError());
return EXIT_FAILURE;
}
if(listen(listening_sock, /* backlog: */1) != 0) {
fprintf(stderr, "Error %d listening on socket\n", WSAGetLastError());
return EXIT_FAILURE;
}
fprintf(stderr, "Listening on TCP port %u at %s\n"
,inet_addrport(&listening_interface), inet_addrtop(&listening_interface, tmp, sizeof(tmp)));
_beginthread(listen_thread, /* stack_size: */0, &modem);
} else {
if(sock != INVALID_SOCKET)
connected(&modem);
}
const char* dropfile = "dosxtrn.env";
FILE* fp = fopen(dropfile, "w");
if(fp == NULL) {
perror(dropfile);
return EXIT_FAILURE;
}
char* dospgm = argv[argn];
for(; argn < argc; argn++) {
fprintf(fp, "%s ", argv[argn]);
}
fputc('\n', fp);
fclose(fp);
while(1) {
sprintf(path, "\\\\.\\mailslot\\sbbsexec\\rd%d", cfg.node_num);
rdslot = CreateMailslot(path
,sizeof(buf)/2 // Maximum message size (0=unlimited)
,0 // Read time-out
,NULL); // Security
if(rdslot!=INVALID_HANDLE_VALUE)
break;
if(cfg.node_num == 0xff) {
fprintf(stderr, "Error %ld creating '%s'\n", GetLastError(), path);
return EXIT_FAILURE;
}
++cfg.node_num;
}
sprintf(path, "sbbsexec_carrier%d", cfg.node_num);
carrier_event = CreateEvent(
NULL // pointer to security attributes
,FALSE // flag for manual-reset event
,FALSE // flag for initial state
,path // pointer to event-object name
);
if(carrier_event == NULL) {
fprintf(stderr, "Error %ld creating '%s'\n", GetLastError(), path);
return EXIT_FAILURE;
}
sprintf(path, "sbbsexec_hangup%d", cfg.node_num);
hangup_event = CreateEvent(
NULL // pointer to security attributes
,FALSE // flag for manual-reset event
,FALSE // flag for initial state (DTR = high)
,path // pointer to event-object name
);
if(hangup_event == NULL) {
fprintf(stderr, "Error %ld creating '%s'\n", GetLastError(), path);
return EXIT_FAILURE;
}
sprintf(path, "sbbsexec_hungup%d", cfg.node_num);
hungup_event = CreateEvent(
NULL // pointer to security attributes
,TRUE // flag for manual-reset event
,TRUE // flag for initial state (DCD = low)
,path // pointer to event-object name
);
if(hungup_event == NULL) {
fprintf(stderr, "Error %ld creating '%s'\n", GetLastError(), path);
return EXIT_FAILURE;
}
STARTUPINFO startup_info={0};
startup_info.cb=sizeof(startup_info);
BOOL x64 = FALSE;
IsWow64Process(GetCurrentProcess(), &x64);
sprintf(fullmodemline, "dosxtrn.exe %s %s %u svdm.ini", dropfile, x64 ? "x64" : "NT", cfg.node_num);
PROCESS_INFORMATION process_info;
if(!CreateProcess(
NULL, // pointer to name of executable module
fullmodemline, // pointer to command line string
NULL, // process security attributes
NULL, // thread security attributes
FALSE, // handle inheritance flag
0, //CREATE_NEW_CONSOLE/*|CREATE_SEPARATE_WOW_VDM*/, // creation flags
NULL, // pointer to new environment block
NULL , // pointer to current directory name
&startup_info, // pointer to STARTUPINFO
&process_info // pointer to PROCESS_INFORMATION
)) {
fprintf(stderr, "Error %ld executing '%s'", GetLastError(), fullmodemline);
return EXIT_FAILURE;
}
printf("Executed '%s' successfully\n", fullmodemline);
CloseHandle(process_info.hThread);
if(cfg.data_rate > 0) {
rx_buflen = max(cfg.data_rate / 100, 1);
rx_delay = (ULONGLONG) (1000 * ((double)rx_buflen / cfg.data_rate));
}
ULONGLONG lastring = 0;
ULONGLONG lasttx = 0;
ULONGLONG lastrx = 0;
int largest_recv = 0;
while(WaitForSingleObject(process_info.hProcess,0) != WAIT_OBJECT_0) {
ULONGLONG now = xp_timer64();
if(modem.online) {
fd_set fds = {0};
FD_SET(sock, &fds);
struct timeval tv = { 0, 0 };
if(now - lastrx >= rx_delay && select(/* ignored: */0, &fds, NULL, NULL, &tv) == 1) {
dprintf("select returned 1");
int rd = recv(sock, buf, rx_buflen, /* flags: */0);
dprintf("recv returned %d", rd);
if(rd <= 0) {
int error = WSAGetLastError();
if(rd == 0 || error == WSAECONNRESET) {
dprintf("Connection reset detected");
disconnect(&modem);
vdd_writestr(&wrslot, response(&modem, NO_CARRIER));
continue;
}
dprintf("Socket error %ld on recv", error);
continue;
}
if(rd > largest_recv)
largest_recv = rd;
vdd_write(&wrslot, buf, rd);
lastrx = now;
}
if(WaitForSingleObject(hangup_event, 0) == WAIT_OBJECT_0) {
dprintf("hangup_event signaled");
disconnect(&modem);
vdd_writestr(&wrslot, response(&modem, NO_CARRIER));
}
} else {
if(modem.ringing) {
if(modem.ringcount < 1)
dprintf("Incoming connection");
if(now - lastring > RING_DELAY) {
dprintf("RING");
vdd_writestr(&wrslot, response(&modem, RING));
lastring = now;
modem.ringcount++;
if(modem.auto_answer > 0 && modem.ringcount >= modem.auto_answer) {
vdd_writestr(&wrslot, answer(&modem));
}
}
}
if(cfg.terminate_on_disconnect) {
dprintf("Terminating process on disconnect");
TerminateProcess(process_info.hProcess, 2112);
}
}
size_t rd = 0;
size_t len = sizeof(buf);
// avail=RingBufFree(&outbuf)/2; // leave room for telnet expansion
// if(len>avail)
// len=avail;
while(rd<len) {
unsigned long waiting = 0;
unsigned long msglen = 0;
GetMailslotInfo(
rdslot, // mailslot handle
NULL, // address of maximum message size
NULL, // address of size of next message
&waiting, // address of number of messages
NULL // address of read time-out
);
if(!waiting)
break;
if(ReadFile(rdslot, buf+rd, len-rd, &msglen, NULL)==FALSE || msglen<1)
break;
rd+=msglen;
}
if(rd) {
if(modem.online) {
if(modem.esc_count) {
if(modem.esc_count >= 3)
modem.esc_count = 0;
else {
if(now - lasttx < guard_time(&modem))
if(*buf == modem.esc)
modem.esc_count += count_esc(&modem, buf, rd);
else
modem.esc_count = 0;
}
} else {
if(now - lasttx > guard_time(&modem))
modem.esc_count = count_esc(&modem, buf, rd);
}
int wr = send(sock, buf, rd, /* flags: */0);
if(wr != rd)
dprintf("Sent %d instead of %d", wr, rd);
else if(cfg.debug)
dprintf("TX: %d bytes", wr);
} else { // Command mode
dprintf("RX command: '%.*s'\n", rd, buf);
if(!modem.echo_off)
vdd_write(&wrslot, buf, rd);
char* response = atmodem_parse(&modem, buf, rd);
if(response != NULL) {
vdd_writestr(&wrslot, response);
SKIP_WHITESPACE(response);
dprintf("Modem response: %s", response);
}
}
lasttx = now;
} else {
if(modem.online && modem.esc_count == 3 && now - lasttx >= guard_time(&modem)) {
dprintf("Entering command mode");
modem.online = false;
modem.esc_count = 0;
vdd_writestr(&wrslot, ok(&modem));
}
}
}
int retval = EXIT_SUCCESS;
fp = fopen("DOSXTRN.RET", "r");
if(fp == NULL) {
perror("DOSXTRN.RET");
} else {
if(fscanf(fp, "%d", &retval) != 1) {
fprintf(stderr, "Error reading return value from DOSXTRN.REG");
retval = EXIT_FAILURE;
}
fclose(fp);
if(retval == -1) {
fprintf(stderr, "DOSXTRN failed to execute '%s': ", dospgm);
fp = fopen("DOSXTRN.ERR", "r");
if(fp == NULL) {
perror("DOSXTRN.ERR");
} else {
char errstr[256] = "";
int errval = 0;
if(fscanf(fp, "%d\n", &errval) == 1) {
fgets(errstr, sizeof(errstr), fp);
truncsp(errstr);
fprintf(stderr, "Error %d (%s)\n", errval, errstr);
} else
fprintf(stderr, "Failed to parse DOSXTRN.ERR\n");
fclose(fp);
}
}
}
if(cfg.debug) {
printf("rx_delay: %lld\n", rx_delay);
printf("rx_buflen: %ld\n", rx_buflen);
printf("largest recv: %d\n", largest_recv);
}
return retval;
}

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.32106.194
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vdmodem", "vdmodem.vcxproj", "{20051597-6298-4098-8F26-E408C2880FE4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x86 = Debug|x86
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{20051597-6298-4098-8F26-E408C2880FE4}.Debug|x86.ActiveCfg = Debug|Win32
{20051597-6298-4098-8F26-E408C2880FE4}.Debug|x86.Build.0 = Debug|Win32
{20051597-6298-4098-8F26-E408C2880FE4}.Release|x86.ActiveCfg = Release|Win32
{20051597-6298-4098-8F26-E408C2880FE4}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {9F30FE81-9971-48D0-AC51-EE3F15248CB0}
EndGlobalSection
EndGlobal
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{20051597-6298-4098-8f26-e408c2880fe4}</ProjectGuid>
<RootNamespace>svdmodem</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>NotSet</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\build\undeprecate.props" />
<Import Project="..\xpdev\xpdev_mt.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\build\undeprecate.props" />
<Import Project="..\xpdev\xpdev_mt.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\build\undeprecate.props" />
<Import Project="..\xpdev\xpdev_mt.props" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
<Import Project="..\build\undeprecate.props" />
<Import Project="..\xpdev\xpdev_mt.props" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<TargetName>svdm</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<TargetName>svdm</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
<TargetName>svdm</TargetName>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
<TargetName>svdm</TargetName>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\xpdev\genwrap.c" />
<ClCompile Include="..\xpdev\sockwrap.c" />
<ClCompile Include="vdmodem.c" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment