net.c
Index
/* net.c: TCP/IP client code.
To build a test-stub, compile with
gcc -DTEST net.c
Add a -DSTRERROR_NOT_DEFINED if strerror() doesn't exist on your machine.
Once the program is compiled, try stuff like "./a.out xcf.berkeley.edu 79"
and then enter "ali" followed by RETURN. Port 79 is the WHOIS or finger
port.
Or do "./a.out stanford.edu 25" and start forging mail. Port 25 is the
sendmail port.
The a.out produced behaves like a poor man's version of line-buffered
telnet.
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/ioctl.h>
#include <string.h>
#include <errno.h>
#include "net.h"
static NetErrnoType net_errno = E_NET_OK;
static int saved_errno = 0;
/* Translations between ``net_errno'' values and human readable strings.
*/
static char *net_syserrlist[] = {
"All was chill"
};
#ifdef STRERROR_NOT_DEFINED
const char *strerror(int errno) { return sys_errlist[errno]; }
#endif
static void NetSetErrno(NetErrnoType e)
{
if(e == E_NET_ERRNO)saved_errno = errno;
net_errno = e;
}
/* NetErrStr()
*--------------------------------------------------------------------
* Returns a diagnostic message for the last failure.
*/
const char *NetErrStr()
{
return net_errno==E_NET_ERRNO ? strerror(saved_errno) :
net_syserrlist[net_errno];
}
/* NetErrNo()
*--------------------------------------------------------------------
* Returns a diagnostic number for the last failure.
*/
NetErrnoType NetErrNo()
{
return net_errno;
}
/* NetMakeContact()
*--------------------------------------------------------------------
* Makes a tcp connection to a host:port pair.
*--------------------------------------------------------------------
* ``Hostname'' can either be in the form of a hostname or an IP address
* represented as a string. If the hostname is not found as it is,
* ``hostname'' is assumed to be an IP address, and it is treated as such.
*
* If the lookup succeeds, a TCP connection is established with the
* specified ``port'' number on the remote host and a stream socket is
* returned.
*
* On any sort of error, an error code can be obtained with @NetErrNo()
* and a message with @NetErrStr().
*/
SOCKET
NetMakeContact(const char *hname, int port)
{
int fd;
struct sockaddr_in addr;
struct hostent *hent;
fd = socket(AF_INET, SOCK_STREAM, 0);
if(fd == -1)
{
NetSetErrno(E_NET_ERRNO);
return -1;
}
hent = gethostbyname(hname);
if(hent == NULL)
addr.sin_addr.s_addr = inet_addr(hname);
else
memcpy(&addr.sin_addr, hent->h_addr, hent->h_length);
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
if(connect(fd, (struct sockaddr *)&addr, sizeof(addr)))
{
NetSetErrno(E_NET_ERRNO);
return -1;
}
NetSetErrno(E_NET_OK);
return fd;
}
#ifdef TEST
int xmit(int fd1, int fd2)
{
int l;
char iobuf[1024];
l = read(fd1, iobuf, sizeof(iobuf));
if(l<=0)return -1;
l = write(fd2, iobuf, l);
if(l<=0)return -1;
return 0;
}
main(int argc, char **argv)
{
SOCKET s;
fd_set readfds;
if(argc != 3) {
fprintf(stderr, "usage: %s hostname port\n", argv[0]); exit(-1);
}
s = NetMakeContact(argv[1], atoi(argv[2]));
if(s==-1) {
fprintf(stderr, "Network error: %s\n", NetErrStr()); exit(-1);
}
/* Make the socket non-blocking */
ioctl(s, FIONBIO, 1);
FD_ZERO(&readfds);
for(;;)
{
FD_SET(0, &readfds);
FD_SET(s, &readfds);
if(select(s+1, &readfds, NULL, NULL, NULL) == -1) break;
if(FD_ISSET(s, &readfds))
if(xmit(s, 1)) break;
if(FD_ISSET(0, &readfds))
if(xmit(0, s)) break;
}
}
#endif