>On Saturday, August 21, 2021 at 12:22:07 AM UTC-7, PoBe wrote:
>> I have an interest in writing a TOPS20 program utilising tcp/ip.
>> I do have a (vanilla) Panda distribution (7.01) up and running
>> with a working Internet connection.
>> I can Telnet into the machine etc. so I have the basics in place.
>
>I hadn't thought about this in terms of TOPS-20 before you asked,
>but there are some references that trace BSD/sockets back to TOPS-20.
>
>Since TENEX traces back to ARPAnet days, it isn't surprising that it also
>traces back early in the days of IP. One reference is that DoD had BSD
>people create the sockets implementation so that they could get IP
>onto their Unix systems, along with their TOPS-20 systems.
The concept of sockets dates to the first days of ARPAnet (using
NCP(*)). Although there were a myriad of various OSs, each generally
had an I/O model similar to UNIX/POSIX open/read/write/close. This
was generally readily transferable to the ARPAnet NCP by thinking of
the ARPAnet connection as a multiplexed serial link (as was widely
available by the late 60s and early 70s. What was "new" was the huge
number of subdevices (64Ki) as opposed to less than (typically) 100.
This was too many devices for the memory-limited OSs to allocate
(i.e., waste memory space on) at build or boot time. Instead, one had
to make a call to the OS to allocate such a subdevice. These became
known as sockets. (I don't know the actual derivation of the name;
most likely it was simply one of a set of connection-oriented names
that wasn't in use for something else.)
(*) What people generally refer to today as NCP was actually the
ARPAnet Host-to-Host protocol, a name that was rapidly dropped with
the start of TCP development in the mid-1970s.
But the real reason for this post is that I include a sample program
to do TCP/IP communication on various systems, including TOPS-20.
Please excuse the huge number of preprocessor options. The intent was
to make the same source program operate on numerous OSs. There are
also hacks to get around various limitations (including the problem
with the LINKER on TOPS-20 actually being the TOPS-10 program (or
close to it). Also, I apologize for the size of the program.
Note that use of UDP requires elevated privileges on TOPS-20, so no
code for that is available here.
I hope that the program comments will provide sufficient context. This
program was last run on twenex and toad-1, probably in 2013 (and
hasn't been touched since). Also, I haven't bothered to chase down
the necessary additional files to make this run. My purpose is to
show some code that does TCP/IP.
- dmw
----------------- snip here ---------------------------------
/* Adapted from ~/develop/time/nettime/c_getnettime.c (21 Dec 2011) */
/* netday - the object of this program is to display the */
/* time of day, as obtained via the DAYTIME protocol (port 13) */
/* of a specified host. The purpose of writing this program */
/* is to explore the use of low-level JSYS calls on TOPS-20. */
/* A specific goal is to have it run on
twenex.org, which does */
/* not have KCC USYS network support. */
/* FIXME: The USE_TCP_OPEN option doesn't work. There is */
/* garbage that looks like a byte pointer and other cruft) in */
/* the receive buffer -- although the receive count is correct. */
/* If we patch things and use a SIN JSYS (instead of a read), */
/* everything looks ok. */
/* BSD sockets work on daisy and toad-1 (and FreeBSD) */
/* JSYS sockets work on toad-1 and on twenex */
/* TCP_OPEN works on twenex, doesn't work on toad-1 (always get timeout) */
#include <stdlib.h>
#include <stdio.h>
/* #if (__STDC_VERSION__ >= 199901L) || (__GNUC__ >= 3) */
/* # include <stdbool.h> */
/* #endif */
#if __STDC__
# include <stdarg.h>
# include <limits.h>
#else
# include <varargs.h>
#endif
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#include <signal.h>
#include <string.h>
#include <time.h>
/* #if ! __bool_true_false_are_defined */
/* typedef unsigned int _Bool; */
/* # define bool _Bool */
# define bool int
# define false 0
# define true 1
# define __bool_true_false_are_defined 1
/* #endif */
#if ! defined (__unix__)
# if defined (__unix)
# define __unix__ __unix
# elif defined (unix)
# define __unix__ unix
# elif defined (__APPLE_CC__)
# define __unix__ __APPLE_CC__
# endif
#endif
#if defined (__COMPILER_KCC__)
# if __STDC__ /* this is our indicator */
# define xKCCLIB_VERSION 6
# else
# define xKCCLIB_VERSION 5
# endif
#endif /* defined (__COMPILER_KCC__) */
#if __STDC__
# define CONST const
#else
# define CONST
#endif
/* These characteristics are assumed by default: */
#define HAVE_UDP 1
#define HAVE_TCP 1
#if SYS_T20
# define HAVE_GTHST 1
# undef HAVE_UDP
# define HAVE_UDP 0
# define USE_BSD_SOCKETS 0
# define USE_JSYS_SOCKETS 1
# define USE_TCP_OPEN 0 /* FIXME: this doesn't work yet */
# define USE_JSYS_READ_WITH_TCP_OPEN (USE_TCP_OPEN && 0)
# include <sys/types.h>
# include <sys/time.h>
# if USE_TCP_OPEN
# include <fcntl.h>
# if xKCCLIB_VERSION < 6
# define O_RDWR (02) /* Open for reading and writing */
# define O_BINARY (0100) /* Open in binary mode (sys-dep) */
# define O_BSIZE_8 (020000) /* Bytesize value: 8-bit */
# endif
# endif
# if xKCCLIB_VERSION >= 6
# include <unistd.h>
# endif /* __STDC__ */
typedef int SOCKET;
# define recv(h, b, l, f) read ((h), (b), (l))
# define CLOSESOCKET(s) close (s)
# define INVALID_SOCKET (-1)
# define SOCKET_ERROR (-1)
# define HAVE_GETHOSTBYNAME 1
# define HAVE_INET_FUNCS (xKCCLIB_VERSION >= 6) /* basic inet_... */
#else /* SYS_T20 */
# define SYS_T20 0
#endif /* SYS_T20 */
#if defined (__unix__)
# include <unistd.h>
# include <sys/types.h>
# include <sys/time.h>
typedef int SOCKET;
# define CLOSESOCKET(s) close (s)
# define INVALID_SOCKET (-1)
# define SOCKET_ERROR (-1)
# define USE_BSD_SOCKETS 1
# define HAVE_GETHOSTBYNAME 1
# define HAVE_INET_FUNCS 1 /* orig/basic inet_... */
#endif
#if (USE_BSD_SOCKETS + USE_JSYS_SOCKETS + USE_TCP_OPEN) != 1
# error "Must select exactly one socket mechanism."
#endif
#include "str789.h"
#if USE_JSYS_SOCKETS || HAVE_GTHST || USE_JSYS_READ_WITH_TCP_OPEN
# include <jsys.h>
# include <monsym.h>
# include <macsym.h>
# define dGTHLA monsym (".gthla")
# define dGTHSN monsym (".gthsn")
# define dPRIOU monsym (".priou")
# define dTCMWH monsym (".tcmwh")
/* Some, but not all, of these names */
/* are defined if we simply replace */
/* the percent by an underscore. So, */
/* we use a different convention. */
# define GJ__NEW monsym ("gj%new")
# define GJ__SHT monsym ("gj%sht")
# define GS__EOF monsym ("gs%eof")
# define OF__BSZ monsym ("of%bsz")
# define OF__MOD monsym ("of%mod")
# define OF__PLN monsym ("of%pln")
# define OF__RD monsym ("of%rd")
# define OF__WR monsym ("of%wr")
# define HALFWORDMASK 0777777
#endif
#if USE_BSD_SOCKETS
# include <sys/socket.h>
# include <netinet/in.h>
#endif
#if HAVE_GETHOSTBYNAME
# include <netdb.h>
# if SYS_T20 && (xKCCLIB_VERSION < 6)
struct in_addr
{
unsigned long s_addr;
};
# else /* SYS_T20 && (xKCCLIB_VERSION < 6) */
# include <netinet/in.h> /* struct in_addr is supposed to be in <arpa/inet.h> */
# endif /* SYS_T20 && (xKCCLIB_VERSION < 6) */
#endif /* HAVE_GETHOSTBYNAME */
#if HAVE_INET_FUNCS
# include <arpa/inet.h>
#else
# define NEED_INET_NTOA 1 /* we fabriate this if we need it */
#endif
#if defined (__APPLE_CC__)
# define HAVE_TIMEOUT_SOCKOPT 1
# if (__APPLE_CC__ < 1495) /* prior to Darwin 7.0 */
typedef int socklen_t;
# endif
#elif defined (__FreeBSD__)
# if (__FreeBSD__ < 5)
typedef uint32_t in_addr_t;
typedef uint16_t in_port_t;
# endif
# define HAVE_TIMEOUT_SOCKOPT 1
#elif defined (__linux__)
# if defined (__GNUC__) && (__GNUC__ >= 3)
# define HAVE_TIMEOUT_SOCKOPT 1
# else
typedef uint32_t in_addr_t;
typedef uint16_t in_port_t;
# endif
#elif SYS_T20
# define HAVE_TIMEOUT_SOCKOPT 0
typedef int socklen_t;
typedef unsigned short in_port_t;
typedef unsigned int in_addr_t;
# define INADDR_NONE ((in_addr_t) -1)
# define ntohs(x) (x)
# define ntohl(x) (x)
# define htons(x) (x)
# define htonl(x) (x)
# if xKCCLIB_VERSION < 6
typedef int ssize_t;
# define INADDR_LOOPBACK (unsigned long) 0x7f000001
# endif
#endif
typedef unsigned long hostnum_tp; /* 32-bit host num - in host order */
#define NULL_HOSTNUM 0
typedef unsigned short portnum_tp; /* 16-bit port num - in host order */
#define NULL_PORTNUM 0
typedef struct
{
hostnum_tp hostnum;
portnum_tp portnum;
} conninfo_tp;
#if COMPILER_KCC
# define decode_host_identifier HHdecode_host_identifier
# define poll_remote_tcp TTpoll_remote_tcp
# define poll_remote_udp UUpoll_remote_udp
# define timeout_handler TTtimeout_handler
#endif
#if ! defined (NI_MAXHOST)
# define NI_MAXHOST 1025
#endif
#if ! defined (EXIT_SUCCESS)
# define EXIT_FAILURE 1
# define EXIT_SUCCESS 0
#endif
#define OPTINDCH '-'
#define PROGNAME "netday"
#define NUL '\0'
#define THOUSAND 1000
#define DFLT_DAYTIME_PORT 13
#define MAX_DAYTIME_MSG 256
enum poll_type { unknown_POLL, udp_POLL, tcp_POLL, rtime_POLL };
enum timeout_type { unknown_TIMEOUT, sock_TIMEOUT, alarm_TIMEOUT,
posix_TIMEOUT, inherent_TIMEOUT };
double timeout = 5; /* in seconds */
bool verbose;
bool showraw;
int debug;
/* default poll type is TCP */
#if HAVE_TCP
enum poll_type poll_method = tcp_POLL;
#elif HAVE_UDP
enum poll_type poll_method = udp_POLL;
#elif HAVE_TCP
enum poll_type poll_method = tcp_POLL;
#else
enum poll_type poll_method = unknown_POLL;
#endif
enum timeout_type timout_method = unknown_TIMEOUT;
void
#if __STDC__
abort_program (int errcode, CONST char *fmt, ...)
#else
abort_program (va_alist)
va_dcl
#endif
{
va_list ap;
#if ! __STDC__
int errcode;
CONST char *fmt;
#endif
#if __STDC__
va_start (ap, fmt);
#else
va_start (ap);
errcode = va_arg (ap, int);
fmt = va_arg (ap, char *);
#endif
if (errcode == -1)
errcode = errno;
#if SYS_T20
if (errcode == -2)
errcode = -1; /* get last error from MONITOR */
#endif
fprintf (stderr, "%s: ", PROGNAME);
if (errcode != 0)
fprintf (stderr, "error: (%d) %s, ", errcode,
strerror (errcode));
vfprintf (stderr, fmt, ap);
fprintf (stderr, "\n");
va_end (ap);
exit (EXIT_FAILURE);
/* NOTREACHED */
}
#if NEED_INET_NTOA
# define Q(addr36, offset) (((addr36) >> (9 * ((offset) - 1))) & 0xff)
char *
# if __STDC__
inet_ntoa (struct in_addr in)
# else
inet_ntoa (in)
struct in_addr in;
# endif
{
static char ntoabuff [20];
sprintf (ntoabuff, "%d.%d.%d.%d",
Q (in.s_addr, 4), Q (in.s_addr, 3), Q (in.s_addr, 2), Q (in.s_addr, 1));
return ntoabuff;
}
#endif /* NEED_INET_NTOA */
#if HAVE_TIMEOUT_SOCKOPT
static struct timeval
# if __STDC__
mk_timeval (double tosecs)
# else
mk_timeval (tosecs)
double tosecs;
# endif
{
struct timeval tv;
tv.tv_sec = (time_t) tosecs;
tv.tv_usec = (long) ((tosecs - tv.tv_sec) * 1e6);
if ((tosecs != 0) && (tv.tv_sec == 0) && (tv.tv_usec == 0))
tv.tv_usec = 1;
return tv;
}
#endif
void
#if __STDC__
report_attempt (CONST char *method, CONST conninfo_tp *cip)
#else
report_attempt (method, cip)
CONST char *method;
CONST conninfo_tp *cip;
#endif
{
struct in_addr ina;
if (! verbose) return;
ina.s_addr = htonl (word32to36 (cip->hostnum));
printf ("Fetching time from %s:%d via %s\n",
inet_ntoa (ina), cip->portnum, method);
}
hostnum_tp
#if __STDC__
decode_host_identifier (CONST char *hostid)
#else
decode_host_identifier (hostid)
CONST char *hostid;
#endif
{
hostnum_tp hostnum;
#if HAVE_GTHST
/* TODO: apparently the GTDOM JSYS would do a better job of this */
{
int result, acs [5];
acs [1] = dGTHSN; /* host name to number */
acs [2] = (int) &hostid [-1];
result = jsys (GTHST, acs);
if (debug >= 2)
fprintf (stderr,
"GTHST jsys => %d, acs[01234] = %12lo %12lo %12lo %12lo %12lo\n",
result, acs [0], acs [1], acs [2], acs [3], acs [4]);
if (result == 2)
{
hostnum = acs [3];
if (debug >= 2)
fprintf (stderr,
"GTHST returns hostnum => %12.12lo\n",
(long) hostnum);
return hostnum;
}
if (debug >= 1)
fprintf (stderr, "GTHST returns error: %d => %s\n",
acs [0], strerror (-1));
}
#endif /* HAVE_GTHST */
#if HAVE_GETHOSTBYNAME
{
struct hostent *hp;
hp = gethostbyname (hostid);
if (hp != NULL)
{
in_addr_t in_addr;
assert (hp->h_addrtype == AF_INET);
assert (sizeof in_addr == hp->h_length);
# if SYS_T20
memcpy (&in_addr, hp->h_addr, sizeof in_addr);
# else
memcpy (&in_addr, hp->h_addr_list [0], sizeof in_addr);
# endif
hostnum = ntohl (word36to32 (in_addr));
if (debug >= 2)
{
struct in_addr sin_addr;
sin_addr.s_addr = in_addr;
fprintf (stderr, "GHBN returns hostnum => %s\n",
inet_ntoa (sin_addr));
}
return hostnum;
}
}
#endif /* HAVE_GETHOSTBYNAME */
#if HAVE_INET_FUNCS
{
struct in_addr sin_addr;
sin_addr.s_addr = inet_addr (hostid);
if (sin_addr.s_addr != INADDR_NONE)
{
hostnum = ntohl (word36to32 (sin_addr.s_addr));
if (debug >= 2)
fprintf (stderr, "inet_addr -> %12.12lo\n",
(long) hostnum);
return hostnum;
}
}
#else /* HAVE_INET_FUNCS */
{
int count;
unsigned int h1, h2, h3, h4;
if (sscanf (hostid, "%u.%u.%u.%u", &h1, &h2, &h3, &h4) == 4)
{
hostnum = 0;
hostnum |= h1 << 24;
hostnum |= h2 << 16;
hostnum |= h3 << 8;
hostnum |= h4 << 0;
if (debug >= 2)
fprintf (stderr, "quad decode -> %12.12lo\n",
(long) hostnum);
return hostnum;
}
}
#endif /* HAVE_INET_FUNCS */
if (debug >= 1)
fprintf (stderr, "didn't match any host name\n");
return NULL_HOSTNUM;
}
void
#if __STDC__
timeout_handler (int signum)
#else
timeout_handler (signum)
int signum;
#endif
{
(void) signum;
abort_program (ETIMEDOUT, "Attempt to contact remote host");
}
void
#if __STDC__
set_alarm_timeout (double tout_secs)
#else
set_alarm_timeout (tout_secs)
double tout_secs;
#endif
{
unsigned int alrm_secs = (unsigned int) tout_secs;
if (debug >= 3)
fprintf (stderr, "Setting alarm for %d secs\n", alrm_secs);
assert (tout_secs != 0);
signal (SIGALRM, timeout_handler);
if (alrm_secs == 0)
alrm_secs = 1;
alarm (alrm_secs);
}
#if HAVE_TIMEOUT_SOCKOPT
void
# if __STDC__
set_socktimeout (SOCKET sockfd, double tout_secs)
# else
set_socktimeout (sockfd, tout_secs)
SOCKET sockfd;
double tout_secs;
# endif
{
struct timeval timout;
timout = mk_timeval (tout_secs);
assert (tout_secs != 0);
if (debug >= 2)
fprintf (stderr, "SO_SNDTIMEO: %ld:%06ld\n",
(long) timout.tv_sec, (long) timout.tv_usec);
if (setsockopt (sockfd, SOL_SOCKET, SO_SNDTIMEO,
(CONST void *) &timout, sizeof timout) != 0)
abort_program (-1, "setsockopt SO_SNDTIMEO");
if (setsockopt (sockfd, SOL_SOCKET, SO_RCVTIMEO,
(CONST void *) &timout, sizeof timout) != 0)
abort_program (-1, "setsockopt SO_RCVTIMEO");
}
#endif /* HAVE_TIMEOUT_SOCKOPT */
void
#if __STDC__
set_timeout (SOCKET sockfd, double tout_secs)
#else
set_timeout (sockfd, tout_secs)
SOCKET sockfd;
double tout_secs;
#endif
{
(void) sockfd;
if (timeout == 0)
return; /* no timeout desired */
if (debug >= 1)
fprintf (stderr, "Setting timeout to %f seconds\n", tout_secs);
switch (timout_method)
{
#if HAVE_TIMEOUT_SOCKOPT
case sock_TIMEOUT:
set_socktimeout (sockfd, timeout);
break;
#endif
case alarm_TIMEOUT:
set_alarm_timeout (timeout);
break;
default:
assert (! "No timeout mechanism known");
}
}
void
#if __STDC__
mkopendescriptor (char *buffp, size_t buffsize, CONST conninfo_tp *cip)
#else
mkopendescriptor (buffp, buffsize, cip)
char *buffp;
size_t buffsize;
CONST conninfo_tp *cip;
#endif
{
sprintf (buffp,
"TCP:.%lo-%d;CONNECTION:ACTIVE", cip->hostnum, cip->portnum);
if (timeout != 0)
{
char *sptr = strchr (buffp, NUL);
sprintf (sptr, ";PERSIST:%d;TIMEOUT:%d",
(int) timeout, (int) timeout);
}
if (debug >= 2)
fprintf (stderr, "open descriptor = '%s'\n", buffp);
assert (strlen (buffp) < buffsize);
}
/* Requires that msg have space for a trailing NUL! */
void
#if __STDC__
display_daytime_msg (char *msgp, size_t msglen)
#else
display_daytime_msg (msgp, msglen)
char *msgp;
size_t msglen;
#endif
{
if (showraw)
fwrite (msgp, 1, msglen, stdout);
else
{
size_t indx;
msgp [msglen] = NUL;
while ((msglen > 0) && isspace (msgp [msglen - 1]))
msgp [--msglen] = NUL;
/* Rid the message of vile characters. */
/* Note that we use the length here in */
/* case there is an embedded NUL. */
for (indx = 0 ; indx < msglen ; ++indx)
{
int ch = msgp [indx];
if (! (isgraph (ch) || isspace (ch)))
msgp [indx] = '?';
}
printf ("%s\n", msgp);
}
}
#if HAVE_TCP
ssize_t
# if __STDC__
read_tcp_data (SOCKET sockfd, char *buffp, size_t buffsize)
# else
read_tcp_data (sockfd, buffp, buffsize)
SOCKET sockfd;
char *buffp;
size_t buffsize;
# endif
{
ssize_t numxferred;
if (debug >= 1)
fprintf (stderr, "Waiting for data from remote host.\n");
if (debug >= 3)
fprintf (stderr, "sockfd at read = %d\n", sockfd);
# if (USE_BSD_SOCKETS || USE_TCP_OPEN) && (! USE_JSYS_READ_WITH_TCP_OPEN)
numxferred = recv (sockfd, buffp, buffsize, 0);
# elif USE_JSYS_SOCKETS || USE_JSYS_READ_WITH_TCP_OPEN
{
int result, acs [5];
ssize_t count1, count2;
#if USE_JSYS_READ_WITH_TCP_OPEN
int jfn;
fprintf (stderr, "Using JSYS read w/ TCP open\n");
jfn = fcntl (sockfd, F_GETSYSFD, 0);
fprintf (stderr, "jfn for sockfd %d is %d\n", sockfd, jfn);
sockfd = jfn;
#endif
acs [1] = sockfd;
acs [2] = (int) &buffp [-1];
acs [3] = buffsize;
if (debug >= 3)
fprintf (stderr,
"SIN acs in [123]: %lo, %12.12lo, %lo\n",
acs [1], acs [2], acs [3]);
result = jsys (SIN, acs);
if (debug >= 3)
fprintf (stderr,
"SIN out [0123]: %lo, %lo, %12.12lo, %lo\n",
acs [0], acs [1], acs [2], acs [3]);
if (debug >= 2)
{
int *p = (int *) buffp;
fprintf (stderr, "rcvd msg: %9.9lx, %9.9lx, %9.9lx\n",
p [0], p [1], p [2]);
}
/* save the count value before it goes away */
count1 = (char *) acs [2] - (buffp - 1);
count2 = buffsize - acs [3];
if (debug >= 3)
{
fprintf (stderr, "count1 = %ld\n", (long) count1);
fprintf (stderr, "count2 = %ld\n", (long) count2);
}
if (result != 1)
{
if (debug >= 1)
fprintf (stderr, "SIN JSYS failed: %d - %s\n",
result, strerror (-1));
acs [1] = sockfd;
if (jsys (GTSTS, acs) != 1)
abort_program (-2, "GTSYS failed");
if ((acs [2] & GS__EOF) != GS__EOF)
{
return SOCKET_ERROR;
}
}
assert (count1 == count2);
numxferred = count1;
}
# else /* USE_BSD_SOCKETS || USE_TCP_OPEN */
# error "invalid #if/#else construct"
# endif /* USE_BSD_SOCKETS || USE_TCP_OPEN */
if (debug >= 2)
fprintf (stderr,
"Received %ld bytes from remote host (errno = %d).\n",
(long) numxferred, errno);
if (debug >= 3)
{
const long *p = (const long *) buffp;
fprintf (stderr, "buffp @ %12.12lo\n", (long) buffp);
fprintf (stderr, "buffp => %9.9lx %9.9lx %9.9lx %9.9lx\n",
p [0], p [1], p [2], p [3]);
fprintf (stderr, "buffp => %12.12lo %12.12lo %12.12lo %12.12lo\n",
p[0], p[1], p[2], p[3]);
}
if (numxferred == SOCKET_ERROR)
return SOCKET_ERROR;
return numxferred;
}
#endif /* HAVE_TCP */
#if HAVE_TCP
bool
# if __STDC__
poll_remote_tcp (SOCKET sockfd, CONST conninfo_tp *cip)
# else
poll_remote_tcp (sockfd, cip)
SOCKET sockfd;
CONST conninfo_tp *cip;
# endif
{
ssize_t numxferred;
char netmsg [MAX_DAYTIME_MSG + 1];
# if USE_BSD_SOCKETS
{
struct sockaddr_in sai;
memset (&sai, 0, sizeof sai);
sai.sin_family = AF_INET;
sai.sin_addr.s_addr = htonl (word32to36 (cip->hostnum));
sai.sin_port = htons (cip->portnum);
if (debug >= 2)
{
fprintf (stderr, "hostnum = %lx\n", (long) cip->hostnum);
fprintf (stderr, "sin_addr = %lx\n", (long) sai.sin_addr.s_addr);
}
if (connect (sockfd, (CONST struct sockaddr *) &sai, sizeof sai) != 0)
{
switch (errno)
{
case ECONNREFUSED:
case ETIMEDOUT:
case EADDRNOTAVAIL:
case ENETUNREACH:
return false;
}
abort_program (-1, "connect");
}
}
# elif USE_JSYS_SOCKETS
{
int acs [5];
(void) cip;
acs [1] = sockfd;
acs [2] = FLD (8, OF__BSZ) | FLD (dTCMWH, OF__MOD)
| OF__RD | OF__WR | OF__PLN;
/* 8 bit bytes, high throughput, read/write */
if (jsys (OPENF, acs) != 2)
{
if (debug >= 1)
fprintf (stderr, "OPENF JSYS failed: %s\n",
strerror (-1));
return false;
}
}
# elif USE_TCP_OPEN
/* nothing to do ? */
# else /* USE_BSD_SOCKETS */
# error "Invalid #if/#else"
# endif /* USE_BSD_SOCKETS */
numxferred = 0;
for ( ; ; )
{
ssize_t numread = read_tcp_data (sockfd, netmsg + numxferred,
sizeof netmsg - 1 - numxferred);
if (numread == SOCKET_ERROR)
return false;
if (numread == 0)
break;
numxferred += numread;
}
display_daytime_msg (netmsg, numxferred);
return true;
}
#endif /* HAVE_TCP */
#if HAVE_TCP
bool
# if __STDC__
fetchtcptime (CONST conninfo_tp *cip)
# else
fetchtcptime (cip)
CONST conninfo_tp *cip;
# endif
{
SOCKET sockfd;
bool result;
report_attempt ("TCP", cip);
if (debug >= 1)
fprintf (stderr, "Creating TCP socket\n");
# if USE_BSD_SOCKETS
sockfd = socket (AF_INET, SOCK_STREAM, 0);
# elif USE_JSYS_SOCKETS
{
int acs [5];
char openname [256];
mkopendescriptor (openname, sizeof openname, cip);
acs [1] = GJ__SHT | GJ__NEW;
acs [2] = (int) &openname [-1];
if (jsys (GTJFN, acs) != 2)
{
if (debug >= 1)
fprintf (stderr, "GTJFN JSYS failed: %s\n",
strerror (-1));
return false;
}
sockfd = acs [1];
}
# elif USE_TCP_OPEN
{
char openbuf [256];
mkopendescriptor (openbuf, sizeof openbuf, cip);
sockfd = open (openbuf, O_RDWR | O_BINARY | O_BSIZE_8);
}
# else /* USE_BSD_SOCKETS */
# error "Invalid #if/#else construct"
# endif /* USE_BSD_SOCKETS */
if (debug >= 2)
fprintf (stderr, "sockfd = %d, errno = %d, lasterr = %s\n",
sockfd, errno, strerror (-1));
if (sockfd == INVALID_SOCKET)
abort_program (-1, "opening socket");
# if ! SYS_T20
set_timeout (sockfd, timeout);
# endif
result = poll_remote_tcp (sockfd, cip);
# if USE_BSD_SOCKETS || USE_TCP_OPEN
CLOSESOCKET (sockfd);
# elif USE_JSYS_SOCKETS
{
int result, acs [5];
acs [1] = (sockfd & HALFWORDMASK);
result = jsys (CLOSF, acs);
}
# else /* USE_BSD_SOCKETS || USE_TCP_OPEN */
# error "invalid #if/#else construct"
# endif /* USE_BSD_SOCKETS || USE_TCP_OPEN */
return result;
}
#endif /* HAVE_TCP */
#if HAVE_UDP
bool
# if __STDC__
poll_remote_udp (SOCKET sockfd, CONST struct sockaddr_in *saip, socklen_t salen)
# else
poll_remote_udp (sockfd, saip, salen)
SOCKET sockfd;
CONST struct sockaddr_in *saip;
socklen_t salen;
# endif
{
ssize_t numxferred;
socklen_t addrsize;
char netmsg [MAX_DAYTIME_MSG + 1];
struct sockaddr_in fromsai;
if (debug >= 1)
fprintf (stderr, "Sending data to remote host.\n");
numxferred = sendto (sockfd, (char *) netmsg, 0, 0,
(struct sockaddr *) saip, salen);
if (numxferred == SOCKET_ERROR)
return false;
if (numxferred != 0)
{
errno = EIO;
return false;
}
if (debug >= 1)
fprintf (stderr, "Waiting for data from remote host.\n");
addrsize = sizeof fromsai;
numxferred = recvfrom (sockfd, (char *) netmsg, sizeof netmsg - 1,
0, (struct sockaddr *) &fromsai, &addrsize);
if (debug >= 2)
fprintf (stderr,
"Received %d bytes from remote host (errno = %d)\n",
(int) numxferred, errno);
if (numxferred == SOCKET_ERROR)
{
if (errno == EAGAIN)
errno = ETIMEDOUT;
return false;
}
display_daytime_msg (netmsg, numxferred);
return true;
}
#endif /* HAVE_UDP */
#if HAVE_UDP
bool
# if __STDC__
fetchudptime (CONST conninfo_tp *cip)
# else
fetchudptime (cip)
CONST conninfo_tp *cip;
# endif
{
SOCKET sockfd;
bool result;
struct sockaddr_in sai;
report_attempt ("UDP", cip);
if (debug >= 1)
fprintf (stderr, "Creating UDP socket\n");
memset (&sai, 0, sizeof sai);
sai.sin_family = AF_INET;
sai.sin_addr.s_addr = htonl (word32to36 (cip->hostnum));
sai.sin_port = htons (cip->portnum);
sockfd = socket (sai.sin_family, SOCK_DGRAM, 0);
if (sockfd == INVALID_SOCKET)
abort_program (-1, "opening socket");
set_timeout (sockfd, timeout);
result = poll_remote_udp (sockfd, &sai, sizeof sai);
CLOSESOCKET (sockfd);
return result;
}
#endif /* HAVE_UDP */
bool
#if __STDC__
fetch_nettime (CONST conninfo_tp *cip)
#else
fetch_nettime (cip)
CONST conninfo_tp *cip;
#endif
{
bool result = false;
switch (poll_method)
{
#if HAVE_TCP
case tcp_POLL:
result = fetchtcptime (cip);
if (! result)
return false;
break;
#endif /* HAVE_TCP */
#if HAVE_UDP
case udp_POLL:
result = fetchudptime (cip);
if (! result)
return false;
break;
#endif /* HAVE_UDP */
default:
assert (! "Internal error: invalid switch in getnetime");
}
return result;
}
hostnum_tp
#if __STDC__
get_loopback_id (void) /* this returns a 32-bit number */
#else
get_loopback_id ()
#endif
{
#if SYS_T20
return gethostid ();
#elif HAVE_GTHST /* this won't get selected, but we save the code */
{
int hostnum, result, acs [5];
acs [1] = dGTHLA; /* get all local addresses */
acs [3] = (int) &hostnum;
acs [4] = 1; /* but we really only want one */
result = jsys (GTHST, acs);
if (debug >= 2)
fprintf (stderr,
"GTHST jsys => %d, acs[01234] = %12lo %12lo %12lo %12lo %12lo\n",
result, acs [0], acs [1], acs [2], acs [3], acs [4]);
if (result == 2)
{
if (debug >= 2)
fprintf (stderr,
"GTHST returns local hostnum => %12.12lo\n",
(long) hostnum);
return hostnum;
}
if (debug >= 1)
fprintf (stderr, "GTHST returns error: %d => %s\n",
acs [0], strerror (-1));
}
#endif /* SYS_T20 */
return INADDR_LOOPBACK;
}
bool
#if __STDC__
getnettime (CONST char *hostname, portnum_tp portnum)
#else
getnettime (hostname, portnum)
CONST char *hostname;
portnum_tp portnum;
#endif
{
conninfo_tp conninfo;
#if SYS_T20
if ((hostname == NULL) || (strcmp (hostname, "localhost") == 0))
#else
if (hostname == NULL)
#endif
{
conninfo.hostnum = get_loopback_id ();
if (debug >= 2)
fprintf (stderr, "NULL hostname => %12.12lo\n",
(long) conninfo.hostnum);
}
else
{
hostnum_tp hostnum;
hostnum = decode_host_identifier (hostname);
if (hostnum == NULL_HOSTNUM)
abort_program (-1, "Unable to decode hostname: %s",
hostname);
conninfo.hostnum = hostnum;
}
conninfo.portnum = portnum;
return fetch_nettime (&conninfo);
}
void
#if __STDC__
prusage (void)
#else
prusage ()
#endif
{
fprintf (stderr, "Displays time at a remote host via DAYTIME protocol.\n");
fprintf (stderr, "Usage is:\n");
fprintf (stderr, " %s [ options ] [ hostname [ timeport ]]\n", PROGNAME);
fprintf (stderr, "Options are\n");
# if HAVE_TCP
fprintf (stderr, " %ct use TCP protocol (default)\n", OPTINDCH);
# endif /* HAVE_TCP */
# if HAVE_UDP
fprintf (stderr, " %cu use UDP protocol\n", OPTINDCH);
# endif /* HAVE_UDP */
fprintf (stderr, " %cv be verbose\n", OPTINDCH);
fprintf (stderr, " %cr emit the raw message as received\n", OPTINDCH);
fprintf (stderr, " %cT secs set timeout\n", OPTINDCH);
fprintf (stderr, " %cA use alarm timeout method\n", OPTINDCH);
# if HAVE_TIMEOUT_SOCKOPT
fprintf (stderr, " %cS use socket timeout method\n", OPTINDCH);
# endif
exit (2);
}
#if SYS_T20 && (xKCCLIB_VERSION < 6)
extern int optind;
extern char *optarg;
#endif
void
#if __STDC__
initialize (int argc, char *argv [])
#else
initialize (argc, argv)
int argc;
char *argv [];
#endif
{
bool errflag = false;
int ch;
while ((ch = getopt (argc, argv, "!ArST:tuvZ")) != -1)
{
switch (ch)
{
case '!': case 'Z':
++debug;
break;
case 'A':
timout_method = alarm_TIMEOUT;
break;
case 'r':
showraw = true;
break;
# if HAVE_TIMEOUT_SOCKOPT
case 'S':
timout_method = sock_TIMEOUT;
break;
# endif
case 'T':
timeout = strtod (optarg, NULL);
break;
# if HAVE_TCP
case 't':
poll_method = tcp_POLL;
break;
# endif /* HAVE_TCP */
# if HAVE_UDP
case 'u':
poll_method = udp_POLL;
break;
# endif /* HAVE_UDP */
case 'v':
verbose = true;
break;
case '?':
default:
errflag = true;
break;
}
}
if (timout_method == unknown_TIMEOUT)
{
# if HAVE_TIMEOUT_SOCKOPT
timout_method = sock_TIMEOUT;
# else
timout_method = alarm_TIMEOUT;
# endif
}
if (errflag)
prusage ();
if (debug >= 1)
{
# if USE_BSD_SOCKETS
fprintf (stderr, "Using BSD sockets\n");
# elif USE_JSYS_SOCKETS
fprintf (stderr, "Using JSYS sockets\n");
# elif USE_TCP_OPEN
fprintf (stderr, "Using special TCP open\n");
# else
# error "Invalid #if/#else case"
# endif
}
}
int
#if __STDC__
main (int argc, char *argv [])
#else
main (argc, argv)
int argc;
char *argv [];
#endif
{
CONST char *hostname = NULL, *hostpname = NULL;
portnum_tp portnum = DFLT_DAYTIME_PORT;
bool result;
initialize (argc, argv);
switch (argc - optind)
{
default:
prusage ();
case 2: portnum = atol (argv [optind + 1]);
/* FALLTHROUGH */
case 1: hostname = argv [optind + 0];
/* FALLTHROUGH */
case 0:
break;
}
hostpname = (hostname != NULL) ? hostname : "(localhost)";
result = getnettime (hostname, portnum);
if (! result)
abort_program (-1, "Unable to get time from %s", hostpname);
return result ? EXIT_SUCCESS : EXIT_FAILURE;
}
----------------- snip here ---------------------------------