home *** CD-ROM | disk | FTP | other *** search
- /*
- * This source file is Copyright 1995 by Evan Scott.
- * All rights reserved.
- * Permission is granted to distribute this file provided no
- * fees beyond distribution costs are levied.
- */
-
- /*
- * a message passing API for amitcp
- */
-
- #include <exec/types.h>
- #include <exec/ports.h>
-
- #include <dos/dos.h>
- #include <dos/dostags.h>
-
- #include <proto/exec.h>
- #include <proto/dos.h>
-
- #define INLINES_AS_MACROS 1 /* SAS doesn't seem to do inlines properly */
- #include <proto/socket.h>
-
- /* these particularly need to be the amitcp includes */
- #include <sys/errno.h>
- #include <sys/ioctl.h>
-
- #include <netdb.h>
- #include <string.h>
-
- #include "evtypes.h"
- #include "verify.h"
-
- #include "tcp.h"
-
- tcpmessage *new_tcpmessage(struct MsgPort *reply_port)
- {
- tcpmessage *z;
-
- z = (tcpmessage *)allocate(sizeof(*z), V_tcpmessage);
- if (!z) return nil;
-
- ensure(z, V_tcpmessage);
-
- z->header.mn_Node.ln_Type = NT_MESSAGE;
- z->header.mn_Node.ln_Pri = 0;
- z->header.mn_Node.ln_Name = "TCPMessage";
- z->header.mn_ReplyPort = reply_port;
- z->header.mn_Length = sizeof(*z);
-
- z->command = TCP_NOOP;
- z->ident = nil;
- z->address.l = 0;
- z->port.w = 0;
-
- z->data = nil;
- z->interrupt = nil;
- z->length = 0;
- z->result = 0;
- z->error = NO_ERROR;
- z->flags = 0;
-
- return z;
- }
-
- void free_tcpmessage(tcpmessage *tm)
- {
- verify(tm, V_tcpmessage);
-
- ensure(tm, 0);
-
- deallocate(tm, V_tcpmessage);
-
- return;
- }
-
- tcpident *new_tcpident(sb32 fd)
- {
- tcpident *ti;
-
- ti = (tcpident *)allocate(sizeof(*ti), V_tcpident);
- if (!ti) return nil;
-
- ensure(ti, V_tcpident);
-
- ti->fd = fd;
- ti->eof = false;
-
- return ti;
- }
-
- void free_tcpident(tcpident *ti)
- {
- verify(ti, V_tcpident);
-
- ensure(ti, 0);
-
- deallocate(ti, V_tcpident);
-
- return;
- }
-
- void fix_read_set(struct List *wait_list, fd_set *reads, sb32 *max_fd)
- {
- tcpmessage *tm;
- tcpident *ti;
-
- FD_ZERO(reads);
- *max_fd = -1;
-
- for (tm = (tcpmessage *)wait_list->lh_Head;
- tm->header.mn_Node.ln_Succ;
- tm = (tcpmessage *)tm->header.mn_Node.ln_Succ) {
- ti = tm->ident;
- verify(ti, V_tcpident);
-
- if (ti->fd > *max_fd) *max_fd = ti->fd;
-
- if (tm->command == TCP_LISTEN || tm->command == TCP_READ) {
- FD_SET(ti->fd, reads);
- break;
- }
- }
- }
-
- void fix_write_set(struct List *wait_list, fd_set *writes, sb32 *max_fd)
- {
- tcpmessage *tm;
- tcpident *ti;
-
- FD_ZERO(writes);
- *max_fd = -1;
-
- for (tm = (tcpmessage *)wait_list->lh_Head;
- tm->header.mn_Node.ln_Succ;
- tm = (tcpmessage *)tm->header.mn_Node.ln_Succ) {
- ti = tm->ident;
- verify(ti, V_tcpident);
-
- if (ti->fd > *max_fd) *max_fd = ti->fd;
-
- if (tm->command == TCP_WRITE) {
- FD_SET(ti->fd, writes);
- break;
- }
- }
- }
-
- void non_blocking(struct Library *SocketBase, sb32 fd)
- {
- long one;
-
- one = 1;
-
- IoctlSocket(fd, FIONBIO, (void *)&one);
- }
-
- void unique_name(void *tp, b8 *s, b8 *buffer)
- {
- b32 task;
-
- task = (b32)tp;
-
- buffer[0] = (task >> 28) & 0xf;
- buffer[1] = (task >> 24) & 0xf;
- buffer[2] = (task >> 20) & 0xf;
- buffer[3] = (task >> 16) & 0xf;
- buffer[4] = (task >> 12) & 0xf;
- buffer[5] = (task >> 8) & 0xf;
- buffer[6] = (task >> 4) & 0xf;
- buffer[7] = task & 0xf;
-
- if (buffer[0] > 9) buffer[0] += 'a' - 10; else buffer[0] += '0';
- if (buffer[1] > 9) buffer[1] += 'a' - 10; else buffer[1] += '0';
- if (buffer[2] > 9) buffer[2] += 'a' - 10; else buffer[2] += '0';
- if (buffer[3] > 9) buffer[3] += 'a' - 10; else buffer[3] += '0';
- if (buffer[4] > 9) buffer[4] += 'a' - 10; else buffer[4] += '0';
- if (buffer[5] > 9) buffer[5] += 'a' - 10; else buffer[5] += '0';
- if (buffer[6] > 9) buffer[6] += 'a' - 10; else buffer[6] += '0';
- if (buffer[7] > 9) buffer[7] += 'a' - 10; else buffer[7] += '0';
-
- strcpy(&buffer[8], s);
- }
-
- void tcp_read(struct Library *SocketBase, tcpmessage *tm, struct List *wait_list, fd_set *reads, sb32 *max_fd)
- {
- tcpident *ti;
- sb32 result;
- b8 *s;
-
- truth(SocketBase != nil);
- truth(max_fd != nil);
- truth(reads != nil);
- truth(wait_list != nil);
-
- verify(tm, V_tcpmessage);
-
- ti = tm->ident;
-
- if (!ti) {
- tm->result = 0;
- tm->error = ERROR_NO_CONNECTION;
-
- ReplyMsg(&tm->header);
- return;
- }
-
- verify(ti, V_tcpident);
-
- if (tm->length == 0) {
- tm->result = 0;
-
- if (ti->eof)
- tm->error = ERROR_EOF;
- else
- tm->error = NO_ERROR;
-
- ReplyMsg(&tm->header);
- return;
- }
-
- /* socket has got to be set to non-blocking */
-
- if (tm->flags & FLAG_READLINE) {
- s = tm->data;
- tm->result = 0;
-
- while (1) {
- result = recv(ti->fd, s, 1, 0);
- if (result == 1) {
- tm->result++;
- if (*s == '\r' || *s == '\n') {
- if (tm->result == 1) { /* blank line ... skip it */
- tm->result--;
- continue;
- }
- tm->error = NO_ERROR;
-
- ReplyMsg(&tm->header);
- return;
- }
- s++;
- if (tm->result == tm->length) {
- tm->error = NO_ERROR;
- ReplyMsg(&tm->header);
- return;
- }
- continue;
- } else if (result == 0) { /* got to be EOF */
- ti->eof = true;
- tm->error = ERROR_EOF;
-
- ReplyMsg(&tm->header);
- return;
- } else {
- switch (Errno()) {
- case EINTR:
- case EWOULDBLOCK: /* nothing there to read yet */
- AddTail(wait_list, (struct Node *)tm);
- FD_SET(ti->fd, reads);
- if (ti->fd > *max_fd) *max_fd = ti->fd;
- return;
- default: /* something went wrong */
- tm->error = ERROR_LOST_CONNECTION;
-
- ReplyMsg(&tm->header);
- }
- return;
- }
- }
- } else {
- result = recv(ti->fd, tm->data, tm->length, 0);
- if (result == tm->length) { /* satisfied immediately */
- tm->result = tm->length;
- tm->error = NO_ERROR;
-
- ReplyMsg(&tm->header);
- return;
- }
- }
-
- if (result == 0) { /* got to be EOF */
- ti->eof = true;
- tm->result = 0;
- tm->error = ERROR_EOF;
-
- ReplyMsg(&tm->header);
- return;
- }
-
- /* from here we have the stuff we have to wait for */
-
- if (result < 0) {
- switch (Errno()) {
- case EINTR:
- case EWOULDBLOCK: /* nothing there to read yet */
- tm->result = 0;
- AddTail(wait_list, (struct Node *)tm);
- FD_SET(ti->fd, reads);
- if (ti->fd > *max_fd) *max_fd = ti->fd;
- return;
- default: /* something went wrong */
- tm->result = 0;
- tm->error = ERROR_LOST_CONNECTION;
-
- ReplyMsg(&tm->header);
- return;
- }
- }
-
- truth(result < tm->length);
-
- tm->result = result;
- AddTail(wait_list, (struct Node *)tm);
- FD_SET(ti->fd, reads);
- if (ti->fd > *max_fd) *max_fd = ti->fd;
- return;
- }
-
- void tcp_write(struct Library *SocketBase, tcpmessage *tm, struct List *wait_list, fd_set *writes, sb32 *max_fd)
- {
- tcpident *ti;
- sb32 result;
-
- truth(SocketBase != nil);
- truth(max_fd != nil);
- truth(writes != nil);
- truth(wait_list != nil);
-
- verify(tm, V_tcpmessage);
-
- ti = tm->ident;
-
- if (!ti) {
- tm->result = 0;
- tm->error = ERROR_NO_CONNECTION;
-
- ReplyMsg(&tm->header);
- return;
- }
-
- verify(ti, V_tcpident);
-
- if (tm->length == 0) {
- tm->result = 0;
- tm->error = NO_ERROR;
-
- ReplyMsg(&tm->header);
- return;
- }
-
- /* socket has got to be set to non-blocking */
-
- result = send(ti->fd, tm->data, tm->length, 0);
- if (result == tm->length) { /* satisfied immediately */
- tm->result = tm->length;
- tm->error = NO_ERROR;
-
- ReplyMsg(&tm->header);
- return;
- }
-
- /* from here we have the stuff we have to wait for */
-
- if (result < 0) {
- switch (Errno()) {
- case EWOULDBLOCK:
- case EINTR: /* write couldn't get through immediately */
- tm->result = 0;
- AddTail(wait_list, (struct Node *)tm);
- FD_SET(ti->fd, writes);
- if (ti->fd > *max_fd) *max_fd = ti->fd;
- return;
- default: /* something has gone wrong */
- tm->result = 0;
- tm->error = ERROR_LOST_CONNECTION;
- ReplyMsg(&tm->header);
- return;
- }
- }
-
- truth(result < tm->length);
-
- tm->result = result;
- AddTail(wait_list, (struct Node *)tm);
- FD_SET(ti->fd, writes);
- if (ti->fd > *max_fd) *max_fd = ti->fd;
- return;
- }
-
- void tcp_read_more(struct Library *SocketBase, tcpmessage *tm, struct List *wait_list, fd_set *reads, sb32 *max_fd)
- {
- tcpident *ti;
- sb32 result;
- b8 *s;
-
- truth(SocketBase != nil);
- truth(max_fd != nil);
- truth(reads != nil);
- truth(wait_list != nil);
-
- verify(tm, V_tcpmessage);
-
- ti = tm->ident;
- verify(ti, V_tcpident);
-
- if (tm->flags & FLAG_READLINE) {
- s = (b8 *)tm->data + tm->result;
-
- while (1) {
- result = recv(ti->fd, s, 1, 0);
- if (result == 1) {
- tm->result++;
- if (*s == '\r' || *s == '\n') {
- if (tm->result == 1) { /* blank line ... skip it */
- tm->result--;
- continue;
- }
- tm->error = NO_ERROR;
-
- Remove((struct Node *)tm);
- ReplyMsg(&tm->header);
-
- fix_read_set(wait_list, reads, max_fd);
- return;
- }
- s++;
- if (tm->result == tm->length) {
- tm->error = NO_ERROR;
-
- Remove((struct Node *)tm);
- ReplyMsg(&tm->header);
-
- fix_read_set(wait_list, reads, max_fd);
- return;
- }
- continue;
- } else if (result == 0) { /* got to be EOF */
- ti->eof = true;
- tm->error = ERROR_EOF;
-
- Remove((struct Node *)tm);
- ReplyMsg(&tm->header);
-
- fix_read_set(wait_list, reads, max_fd);
- return;
- } else {
- switch (Errno()) {
- case EINTR:
- case EWOULDBLOCK: /* nothing there to read yet */
- return;
- default: /* something went wrong */
- tm->error = ERROR_LOST_CONNECTION;
-
- Remove((struct Node *)tm);
- ReplyMsg(&tm->header);
-
- fix_read_set(wait_list, reads, max_fd);
- return;
- }
- return;
- }
- }
- } else {
- result = recv(ti->fd, (b8 *)tm->data + tm->result, tm->length - tm->result, 0);
- if (result == tm->length - tm->result) { /* satisfied! */
- tm->result = tm->length;
- tm->error = NO_ERROR;
-
- Remove((struct Node *)tm); /* remove from wait_list */
- ReplyMsg(&tm->header); /* send it back completed */
-
- fix_read_set(wait_list, reads, max_fd);
-
- return;
- }
-
- if (result == 0) { /* got to be EOF */
- ti->eof = true;
- tm->error = ERROR_EOF;
-
- Remove((struct Node *)tm); /* as above */
- ReplyMsg(&tm->header);
-
- fix_read_set(wait_list, reads, max_fd);
-
- return;
- }
-
- /* have to wait some more */
-
- if (result < 0) {
- switch (Errno()) {
- case EINTR:
- case EWOULDBLOCK: /* nothing more to read yet */
- return;
- default: /* something went wrong */
- tm->error = ERROR_LOST_CONNECTION;
-
- Remove((struct Node *)tm);
- ReplyMsg(&tm->header);
-
- fix_read_set(wait_list, reads, max_fd);
-
- return;
- }
- }
-
- truth(result < tm->length - tm->result);
-
- tm->result += result;
- return; /* keep waiting */
- }
- }
-
- void tcp_write_more(struct Library *SocketBase, tcpmessage *tm, struct List *wait_list, fd_set *writes, sb32 *max_fd)
- {
- tcpident *ti;
- sb32 result;
-
- truth(SocketBase != nil);
- truth(max_fd != nil);
- truth(writes != nil);
- truth(wait_list != nil);
-
- verify(tm, V_tcpmessage);
-
- ti = tm->ident;
- verify(ti, V_tcpident);
-
- result = send(ti->fd, (b8 *)tm->data + tm->result, tm->length - tm->result, 0);
- if (result == tm->length - tm->result) { /* satisfied! */
- tm->result = tm->length;
- tm->error = NO_ERROR;
-
- Remove((struct Node *)tm);
- ReplyMsg(&tm->header);
-
- fix_write_set(wait_list, writes, max_fd);
-
- return;
- }
-
- /* from here we have the stuff we have to wait some more for */
-
- if (result < 0) {
- switch (Errno()) {
- case EWOULDBLOCK:
- case EINTR: /* write blocked again */
- return;
- default: /* something has gone wrong */
- tm->error = ERROR_LOST_CONNECTION;
-
- Remove((struct Node *)tm);
- ReplyMsg(&tm->header);
-
- fix_write_set(wait_list, writes, max_fd);
-
- return;
- }
- }
-
- truth(result < tm->length - tm->result);
-
- tm->result += result;
- return;
- }
-
- void tcp_listen(struct Library *SocketBase, tcpmessage *tm, struct List *wait_list, fd_set *reads, sb32 *max_fd)
- {
- tcpident *ti;
- sb32 result, socklen;
- struct sockaddr_in sin;
- struct hostent *he;
- tcpmessage *wait_tm;
-
- truth(SocketBase != nil);
- truth(max_fd != nil);
- truth(reads != nil);
- truth(wait_list != nil);
-
- verify(tm, V_tcpmessage);
-
- if (tm->ident) {
- tm->result = false;
- tm->error = ERROR_ALREADY_CONNECTED;
-
- ReplyMsg(&tm->header);
- return;
- }
-
- memset(&sin, 0, sizeof(sin));
-
- he = gethostbyname("localhost");
- if (!he) {
- tm->result = false;
- tm->error = ERROR_UNKNOWN_HOST;
-
- ReplyMsg(&tm->header);
- return;
- }
-
- sin.sin_family = he->h_addrtype;
- sin.sin_port = tm->port.w;
- memcpy(&sin.sin_addr.s_addr, he->h_addr_list[0], he->h_length);
-
- ti = new_tcpident(0); /* filled in later */
- if (ti) {
- wait_tm = new_tcpmessage(nil);
- if (wait_tm) {
- result = socket(AF_INET, SOCK_STREAM, 0);
- if (result >= 0) {
- ti->fd = result;
-
- result = bind(ti->fd, (struct sockaddr *)&sin, sizeof(sin));
- if (result == 0) {
- result = listen(ti->fd, 5); /* random msq queue length */
- if (result == 0) {
- /*
- * strictly, we shouldn't need this
- * ... but, we might (better safe etc)
- */
- non_blocking(SocketBase, ti->fd);
-
- socklen = sizeof(sin);
- result = getsockname(ti->fd, (struct sockaddr *)&sin, &socklen);
-
- /* we are listening! */
- tm->ident = ti;
- tm->port.w = sin.sin_port;
-
- wait_tm->ident = ti;
-
- wait_tm->command = TCP_LISTEN;
- wait_tm->port.w = sin.sin_port; /* just for curiosity */
-
- /* note the reply port so we can send future accepts
- to the same place */
- wait_tm->header.mn_ReplyPort = tm->header.mn_ReplyPort;
-
- if (ti->fd > *max_fd) *max_fd = ti->fd;
- FD_SET(ti->fd, reads);
- AddTail(wait_list, (struct Node *)wait_tm);
-
- tm->result = true;
- tm->error = NO_ERROR;
- ReplyMsg(&tm->header);
- return;
- } else {
- tm->error = ERROR_ACCESS_DENIED;
- }
- } else {
- tm->error = ERROR_ACCESS_DENIED;
- }
- CloseSocket(ti->fd);
- } else tm->error = ERROR_OOM;
- free_tcpmessage(wait_tm);
- } else tm->error = ERROR_OOM;
- free_tcpident(ti);
- } else tm->error = ERROR_OOM;
-
- tm->result = false;
-
- ReplyMsg(&tm->header);
- return;
- }
-
- void tcp_accept(struct Library *SocketBase, tcpmessage *tm, struct List *wait_list, struct MsgPort *replies)
- {
- tcpident *ti, *accept_ti;
- tcpmessage *accept_tm, *wait_tm;
- struct sockaddr_in sin;
- sb32 sin_len, result;
-
- truth(SocketBase != nil);
- truth(wait_list != nil);
- truth(replies != nil);
-
- verify(tm, V_tcpmessage);
-
- ti = tm->ident;
- verify(ti, V_tcpident);
-
- sin_len = sizeof(sin);
-
- result = accept(ti->fd, (struct sockaddr *)&sin, &sin_len);
- if (result < 0) { /* this should never really happen I don't think, but ... */
- /* nothing need be done */
- return;
- }
-
- non_blocking(SocketBase, result);
-
- accept_tm = new_tcpmessage(tm->header.mn_ReplyPort);
- if (accept_tm) {
- wait_tm = new_tcpmessage(nil);
- if (wait_tm) {
- accept_ti = new_tcpident(result);
- if (accept_ti) {
- /* all systems go! */
-
- /* tell the parent that we have a new connection ... */
- accept_tm->command = TCP_ACCEPTED;
- accept_tm->address.l = sin.sin_addr.s_addr;
- accept_tm->port.w = sin.sin_port;
-
- accept_tm->ident = accept_ti;
- accept_tm->result = true;
- accept_tm->error = NO_ERROR;
-
- PutMsg(tm->header.mn_ReplyPort, &accept_tm->header);
-
- /* keep a copy on our files for book keeping purposes */
- wait_tm->command = TCP_CONNECTED;
- wait_tm->address.l = sin.sin_addr.s_addr;
- wait_tm->port.w = sin.sin_port;
-
- wait_tm->ident = accept_ti;
-
- /* this is a bit nasty ... it has to be on the head because
- we are traversing the list forward */
- AddHead(wait_list, (struct Node *)wait_tm);
- return;
- }
- free_tcpmessage(wait_tm);
- }
- free_tcpmessage(accept_tm);
- }
-
- /* this is a bit sad ... it accepts and then it just closes for no
- apparent reason ... I don't see any alternative however */
-
- CloseSocket(result);
- return;
- }
-
- void tcp_close(struct Library *SocketBase, tcpmessage *tm, struct List *wait_list, fd_set *reads, fd_set *writes, sb32 *max_fd)
- {
- tcpident *ti, *iti;
- tcpmessage *itm, *nitm;
- int ncons = 0, nlis = 0;
-
- truth(SocketBase != nil);
- truth(max_fd != nil);
- truth(reads != nil);
- truth(wait_list != nil);
-
- verify(tm, V_tcpmessage);
-
- ti = tm->ident;
- if (!ti || ti->connecting_port) {
- tm->result = false;
- tm->error = ERROR_NO_CONNECTION;
-
- ReplyMsg(&tm->header);
- return;
- }
-
- verify(ti, V_tcpident);
-
- /*
- * search through the waiting list and:
- * abort reads and writes that match
- * close TCP_CONNECTEDs
- * close TCP_LISTENs
- */
-
- for (itm = (tcpmessage *)wait_list->lh_Head;
- nitm = (tcpmessage *)itm->header.mn_Node.ln_Succ;
- itm = nitm) {
-
- verify(itm, V_tcpmessage);
-
- switch (itm->command) {
- case TCP_READ:
- case TCP_WRITE:
- iti = itm->ident;
- verify(iti, V_tcpident);
-
- if (iti == ti) {
- itm->error = ERROR_INTERRUPTED;
- itm->ident = nil;
-
- Remove((struct Node *)itm);
- ReplyMsg(&itm->header);
- }
- break;
- case TCP_LISTEN:
- iti = itm->ident;
- verify(iti, V_tcpident);
-
- if (iti == ti) {
- nlis++;
-
- Remove((struct Node *)itm);
- free_tcpmessage(itm);
- }
- break;
- case TCP_CONNECTED:
- iti = itm->ident;
- verify(iti, V_tcpident);
-
- if (iti == ti) {
- ncons++;
-
- Remove((struct Node *)itm);
- free_tcpmessage(itm);
- }
- break;
- }
- }
-
- truth(ncons + nlis == 1); /* one, and only one connection!!! */
-
- if (ti->fd >= 0)
- CloseSocket(ti->fd);
-
- free_tcpident(ti);
-
- tm->ident = nil;
- tm->result = true;
- tm->error = NO_ERROR;
-
- ReplyMsg(&tm->header);
-
- fix_read_set(wait_list, reads, max_fd);
- fix_write_set(wait_list, writes, max_fd);
-
- return;
- }
-
- void do_connect(struct Library *SocketBase, tcpmessage *mess, struct MsgPort *pport, struct MsgPort *myport)
- {
- /* bits of the following inspired by the source to NcFTP ... thanks go to Mike Gleason */
- struct sockaddr_in sin;
- struct hostent *he;
- sb32 result, s;
-
- truth(SocketBase != nil);
- truth(pport != nil);
- truth(myport != nil);
- verify(mess, V_tcpmessage);
-
- memset((void *)&sin, 0, sizeof(&sin)); /* not sure this is necessary ... ncftp does this. JIC */
-
- sin.sin_port = mess->port.w;
- sin.sin_family = AF_INET;
-
- if (mess->data) { /* if they don't send a string, their address must be right */
-
- sin.sin_addr.s_addr = inet_addr(mess->data);
- if (sin.sin_addr.s_addr == -1) { /* not a direct dotted IP number */
- he = gethostbyname(mess->data);
- if (!he) { /* oh well ... */
- mess->result = 0;
- mess->error = ERROR_UNKNOWN_HOST;
-
- PutMsg(pport, &mess->header);
-
- WaitPort(myport);
- GetMsg(myport);
- return;
- }
-
- sin.sin_family = he->h_addrtype;
- memcpy(&sin.sin_addr, he->h_addr_list[0], he->h_length);
- }
- } else {
- sin.sin_addr.s_addr = mess->address.l;
- }
-
- /* we have the address, now try the connect */
-
- s = socket(sin.sin_family, SOCK_STREAM, 0); /* 0 for protocol legal? ncftp does it */
- if (s < 0) {
- mess->result = 0;
- mess->error = ERROR_OOM;
-
- PutMsg(pport, &mess->header);
-
- WaitPort(myport);
- GetMsg(myport);
- return;
- }
-
- result = connect(s, (struct sockaddr *)&sin, sizeof(sin));
- if (result < 0) {
- /* perhaps we should try the backup addresses, but nahhhh */
- switch (Errno()) {
- case ENETDOWN:
- case ENETUNREACH:
- mess->error = ERROR_UNREACHABLE;
- break;
- case ECONNREFUSED:
- mess->error = ERROR_CONNECT_REFUSED;
- break;
- default:
- mess->error = ERROR_CANT_CONNECT;
- break;
- }
-
- CloseSocket(s);
-
- mess->result = 0;
-
- PutMsg(pport, &mess->header);
-
- WaitPort(myport);
- GetMsg(myport);
- return;
- }
-
- /* hurrah */
-
- /* now we have to transfer our socket to our parents "library domain" */
-
- result = ReleaseSocket(s, UNIQUE_ID);
- if (result < 0) { /* BUGGER! just as everything was going so well too :( */
- mess->result = 0;
- mess->error = ERROR_OOM;
-
- CloseSocket(s);
- } else {
- mess->result = result;
- mess->error = NO_ERROR;
- }
-
- PutMsg(pport, &mess->header);
-
- WaitPort(myport);
- GetMsg(myport);
- return;
- }
-
- void __saveds __asm connect_child(register __a0 b8 *parent_port)
- {
- struct Library *SocketBase;
- struct MsgPort *pport, *myport;
- tcpmessage *mess;
-
- pport = FindPort(parent_port);
- if (!pport) {
- /* not a fuck of a lot we can do */
- return;
- }
-
- myport = CreatePort(0, 0);
- if (myport) {
- mess = new_tcpmessage(myport);
- if (mess) {
- SocketBase = OpenLibrary("bsdsocket.library", 0);
- if (SocketBase) {
- /* tell them we are going */
- mess->command = TCP_STARTUP;
- mess->result = true;
- mess->error = NO_ERROR;
-
- PutMsg(pport, &mess->header);
-
- WaitPort(myport);
- GetMsg(myport); /* should be guaranteed to be mess back */
-
- pport = mess->header.mn_ReplyPort; /* may be a different port */
- mess->command = TCP_CONNECTED;
- mess->header.mn_ReplyPort = myport; /* GOT TO BE THE SAME !!! (for ident purposes) */
-
- do_connect(SocketBase, mess, pport, myport);
-
- CloseLibrary(SocketBase);
- } else {
- /* tell them we are NOT going */
- mess->command = TCP_STARTUP;
- mess->result = false;
- mess->error = ERROR_NO_CONNECTION;
-
- PutMsg(pport, &mess->header);
-
- WaitPort(myport);
- GetMsg(myport);
- }
- free_tcpmessage(mess);
- DeletePort(myport);
- return;
- }
- DeletePort(myport);
- }
-
- /* our half-hearted way of telling the parent something is wrong */
- Signal(pport->mp_SigTask, 1 << pport->mp_SigBit);
-
- return;
- }
-
- void tcp_connect(struct Library *SocketBase, tcpmessage *tm, struct List *wait_list, struct MsgPort *replies)
- {
- struct Process *child;
- b8 buffer[30];
- struct MsgPort *tmp_port;
- tcpmessage *child_tm;
-
- unique_name(FindTask(0), ": Evans TCP Handler", buffer);
-
- if (tm->ident) {
- tm->result = false;
- tm->error = ERROR_ALREADY_CONNECTED;
-
- ReplyMsg(&tm->header);
- return;
- }
-
- tm->ident = new_tcpident(-1);
- if (!tm->ident) {
- tm->result = false;
- tm->error = ERROR_OOM;
-
- ReplyMsg(&tm->header);
- return;
- }
-
- tmp_port = CreatePort(buffer, 0);
- if (!tmp_port) {
- free_tcpident(tm->ident);
- tm->ident = nil;
-
- tm->result = false;
- tm->error = ERROR_OOM;
-
- ReplyMsg(&tm->header);
- return;
- }
-
- /* start child */
-
- child = CreateNewProcTags(
- NP_Entry, connect_child,
- NP_Name, "TCP Connect Handler",
- NP_Arguments, buffer,
- TAG_END, 0
- );
-
- if (!child) {
- tm->result = false;
- tm->error = ERROR_OOM;
-
- DeletePort(tmp_port);
- free_tcpident(tm->ident);
- tm->ident = nil;
-
- ReplyMsg(&tm->header);
- return;
- }
-
- /* the child should get back to us immediately ... so
- synchronously wait for the startup message */
-
- Wait(1 << tmp_port->mp_SigBit);
-
- child_tm = (tcpmessage *)GetMsg(tmp_port);
- if (!child_tm) { /* they had a problem, and failed */
- tm->result = false;
- tm->error = ERROR_OOM;
-
- DeletePort(tmp_port);
- free_tcpident(tm->ident);
- tm->ident = nil;
-
- ReplyMsg(&tm->header);
- return;
- }
-
- if (!child_tm->result) { /* ditto */
- tm->error = child_tm->error;
-
- ReplyMsg((struct Message *)child_tm);
-
- tm->result = false;
-
- DeletePort(tmp_port);
- free_tcpident(tm->ident);
- tm->ident = nil;
-
- ReplyMsg(&tm->header);
- return;
- }
-
- /* well, all should be ok now */
-
- DeletePort(tmp_port);
-
- tmp_port = child_tm->header.mn_ReplyPort;
-
- child_tm->header.mn_ReplyPort = replies;
- child_tm->data = tm->data;
- child_tm->flags = tm->flags;
- child_tm->port = tm->port;
- child_tm->address = tm->address;
-
- PutMsg(tmp_port, &child_tm->header);
-
- /* that will come back when the child has resolved the connect */
-
- ((tcpident *)tm->ident)->connecting_port = tmp_port; /* note: this is a special case for TCP_CONNECT ONLY */
-
- AddTail(wait_list, (struct Node *)tm);
-
- return;
- }
-
- void tcp_connected(struct Library *SocketBase, tcpmessage *tm, struct List *wait_list)
- {
- tcpident *ti;
- tcpmessage *itm, *nitm, *wait_tm;
- struct MsgPort *their_port; /* to identify which child */
- sb32 s;
-
- truth(SocketBase != nil);
- truth(wait_list != nil);
- verify(tm, V_tcpmessage);
-
- their_port = tm->header.mn_ReplyPort;
-
- if (tm->error == NO_ERROR) {
- s = ObtainSocket(tm->result, AF_INET, SOCK_STREAM, 0); /* shudder */
- } else {
- s = -1;
- }
-
- /* it may have failed ... but we have to check after we have found the TCP_CONNECT */
-
- /* see which TCP_CONNECT in the wait list it corresponds to ... */
-
- for (itm = (tcpmessage *)wait_list->lh_Head;
- nitm = (tcpmessage *)itm->header.mn_Node.ln_Succ;
- itm = nitm) {
- if (itm->command == TCP_CONNECT) {
- ti = itm->ident;
- verify(ti, V_tcpident);
-
- if (ti->connecting_port == (void *)their_port) {
- Remove((struct Node *)itm);
-
- if ((tm->error != NO_ERROR) || (s < 0)) {
- if (tm->error != NO_ERROR) {
- itm->result = false;
- itm->error = tm->error;
- } else { /* this case is our Obtain failing */
- itm->result = false;
- /* bizarre error for bizarre case */
- itm->error = ERROR_LOST_CONNECTION;
- }
-
- ReplyMsg(&tm->header);
-
- /* NB: reusing tm */
- tm = itm->interrupt;
- itm->interrupt = nil;
-
- free_tcpident(ti);
- itm->ident = nil;
-
- ReplyMsg(&itm->header);
- if (tm) {
- verify(tm, V_tcpmessage);
- ReplyMsg(&tm->header);
- }
- return;
- }
-
- non_blocking(SocketBase, s);
-
- /* ok, have to make a tcpmessage to wait here */
-
- wait_tm = new_tcpmessage(nil);
- if (wait_tm) {
- ti->connecting_port = nil;
- ti->fd = s;
-
- wait_tm->ident = ti;
- wait_tm->command = TCP_CONNECTED;
-
- wait_tm->address = tm->address;
- wait_tm->port = tm->port;
-
- itm->address = tm->address;
- itm->port = tm->port;
-
- itm->result = true;
- itm->error = NO_ERROR;
-
- ReplyMsg(&tm->header);
-
- tm = itm->interrupt;
- itm->interrupt = nil;
-
- ReplyMsg(&itm->header);
-
- if (tm) {
- verify(tm, V_tcpmessage);
- ReplyMsg(&tm->header);
- }
-
- AddTail(wait_list, (struct Node *)wait_tm);
-
- return;
- } else itm->error = ERROR_OOM;
-
- free_tcpident(ti);
- itm->ident = nil;
-
- CloseSocket(s);
- itm->result = false;
-
- ReplyMsg(&tm->header);
-
- tm = itm->interrupt;
- itm->interrupt = nil;
-
- ReplyMsg(&itm->header);
- if (tm) {
- verify(tm, V_tcpmessage);
- ReplyMsg(&tm->header);
- }
-
- return;
- }
- }
- }
-
- /* hmmm, strange things are happening */
- if (s >= 0) CloseSocket(s);
-
- ReplyMsg(&tm->header);
- return;
- }
-
- void tcp_interrupt(tcpmessage *tm, struct List *wait_list, fd_set *reads, fd_set *writes, sb32 *max_fd)
- {
- tcpident *ti;
- tcpmessage *itm, *nitm;
- struct MsgPort *port;
-
- verify(tm, V_tcpmessage);
-
- for (itm = (tcpmessage *)wait_list->lh_Head;
- nitm = (tcpmessage *)itm->header.mn_Node.ln_Succ;
- itm = nitm) {
-
- verify(itm, V_tcpmessage);
-
- if (itm == tm->interrupt) {
- switch (itm->command) {
- case TCP_READ:
- Remove((struct Node *)itm);
-
- itm->error = ERROR_INTERRUPTED;
- ReplyMsg(&itm->header);
-
- tm->result = true;
- tm->error = NO_ERROR;
-
- ReplyMsg(&tm->header);
-
- fix_read_set(wait_list, reads, max_fd);
- return;
- case TCP_WRITE:
- Remove((struct Node *)itm);
-
- itm->error = ERROR_INTERRUPTED;
- ReplyMsg(&itm->header);
-
- tm->result = true;
- tm->error = NO_ERROR;
-
- ReplyMsg(&tm->header);
-
- fix_write_set(wait_list, writes, max_fd);
- return;
- case TCP_CONNECT:
- /* this is the fun one */
- ti = itm->ident;
- verify(ti, V_tcpident);
-
- port = ti->connecting_port;
-
- itm->interrupt = tm;
- Signal(port->mp_SigTask, SIGBREAKF_CTRL_C);
- return;
- }
- }
- }
-
- tm->result = false;
- tm->error = ERROR_NO_CONNECTION;
-
- ReplyMsg(&tm->header);
- return;
- }
-
- void tcp_peername(struct Library *SocketBase, tcpmessage *tm)
- /*
- * this one is a strange one ... it may have the potential to block
- * I don't really want to write a bloody child process thingy just for
- * this trivial bloody function
- */
- {
- tcpident *ti;
- struct sockaddr_in sin;
- struct hostent *he;
- sb32 sin_len, len;
-
- truth(SocketBase != nil);
- verify(tm, V_tcpmessage);
-
- ti = tm->ident;
- if (!ti) {
- tm->result = false;
- tm->error = ERROR_NO_CONNECTION;
-
- ReplyMsg(&tm->header);
- return;
- }
-
- verify(ti, V_tcpident);
-
- sin_len = sizeof(sin);
-
- if (getpeername(ti->fd, (struct sockaddr *)&sin, &sin_len) < 0) {
- if (Errno() == ENOTCONN) {
- tm->error = ERROR_NO_CONNECTION;
- } else {
- tm->error = ERROR_OOM;
- }
- tm->result = false;
-
- ReplyMsg(&tm->header);
- return;
- }
-
- he = gethostbyaddr((void *)&sin.sin_addr.s_addr, 4, AF_INET);
- if (!he) {
- tm->error = ERROR_UNKNOWN_HOST;
- tm->result = false;
-
- ReplyMsg(&tm->header);
- return;
- }
-
- len = strlen(he->h_name);
- if (len >= tm->length) {
- len = tm->length - 1;
- }
-
- memcpy(tm->data, he->h_name, len);
- ((b8 *)tm->data)[len] = 0;
-
- tm->error = NO_ERROR;
- tm->result = true;
-
- ReplyMsg(&tm->header);
- return;
- }
-
- void tcp_service(struct Library *SocketBase, tcpmessage *tm)
- {
- struct servent *se;
-
- truth(SocketBase != nil);
- verify(tm, V_tcpmessage);
-
- se = getservbyname(tm->data, "tcp");
- if (!se) {
- tm->result = false;
- tm->error = ERROR_UNKNOWN_COMMAND;
- } else {
- tm->result = true;
- tm->error = NO_ERROR;
-
- tm->port.w = se->s_port;
- }
-
- ReplyMsg(&tm->header);
- return;
- }
-
- struct MsgPort *running_running(tcpmessage *emergency, struct MsgPort *commands)
- {
- b32 tcp_signal, signal_tmp;
- tcpmessage *tm, *tm2;
- tcpident *ti;
- struct Library *SocketBase = nil;
- struct List waiting;
- fd_set read_set, write_set, read_tmp, write_tmp;
- sb32 max_fd, n;
- struct MsgPort *death_port;
-
- NewList(&waiting); /* list of waiting requests */
-
- FD_ZERO(&read_set);
- FD_ZERO(&write_set);
-
- max_fd = -1;
-
- tcp_signal = (1 << commands->mp_SigBit);
-
- while (1) {
- if (SocketBase) {
- read_tmp = read_set;
- write_tmp = write_set;
- signal_tmp = tcp_signal;
-
- n = WaitSelect(max_fd + 1, &read_tmp, &write_tmp, nil, nil, &signal_tmp);
- } else {
- n = 0;
- Wait(tcp_signal);
- }
-
- while (tm = (tcpmessage *)GetMsg(commands)) {
- /* a message from our parent (sponsor?) */
-
- verify(tm, V_tcpmessage);
-
- if (tm->header.mn_ReplyPort == commands) {
- /* its been ReplyMsg'd to us */
- free_tcpmessage(tm);
- continue;
- }
-
- switch (tm->command) {
- case TCP_NOOP:
- tm->result = true;
- ReplyMsg(&tm->header);
- break;
- case TCP_CONNECT:
- if (!SocketBase) {
- SocketBase = OpenLibrary("bsdsocket.library", 0);
- if (!SocketBase) {
- tm->result = false;
- tm->error = ERROR_NO_CONNECTION;
-
- ReplyMsg(&tm->header);
- break;
- }
- }
- tcp_connect(SocketBase, tm, &waiting, commands);
- break;
- case TCP_LISTEN:
- if (!SocketBase) {
- SocketBase = OpenLibrary("bsdsocket.library", 0);
- if (!SocketBase) {
- tm->result = false;
- tm->error = ERROR_NO_CONNECTION;
-
- ReplyMsg(&tm->header);
- break;
- }
- }
- tcp_listen(SocketBase, tm, &waiting, &read_set, &max_fd);
- break;
- case TCP_READ:
- if (!SocketBase) { /* someone is frigging us around */
- tm->result = 0;
- tm->error = ERROR_NO_CONNECTION;
-
- ReplyMsg(&tm->header);
- break;
- }
- tcp_read(SocketBase, tm, &waiting, &read_set, &max_fd);
- break;
- case TCP_WRITE:
- if (!SocketBase) { /* someone is frigging us around */
- tm->result = 0;
- tm->error = ERROR_NO_CONNECTION;
-
- ReplyMsg(&tm->header);
- break;
- }
- tcp_write(SocketBase, tm, &waiting, &write_set, &max_fd);
- break;
- case TCP_CLOSE:
- if (!SocketBase) { /* someone is frigging us around */
- tm->result = false;
- tm->error = ERROR_NO_CONNECTION;
-
- ReplyMsg(&tm->header);
- break;
- }
- tcp_close(SocketBase, tm, &waiting, &read_set, &write_set, &max_fd);
-
- if (IsListEmpty(&waiting)) {
- /* absolutely everything has closed. We can close
- the socket library now in safety */
- CloseLibrary(SocketBase);
- SocketBase = nil;
- }
- break;
- case TCP_CONNECTED:
- /* ahhh, a child is reporting back! */
-
- if (!SocketBase) { /* someone is frigging us around */
- tm->result = false;
- tm->error = ERROR_NO_CONNECTION;
-
- ReplyMsg(&tm->header);
- break;
- }
-
- tcp_connected(SocketBase, tm, &waiting);
-
- if (IsListEmpty(&waiting)) {
- /* absolutely everything has closed. We can close
- the socket library now in safety */
- CloseLibrary(SocketBase);
- SocketBase = nil;
- }
- break;
- case TCP_INTERRUPT:
- tcp_interrupt(tm, &waiting, &read_set, &write_set, &max_fd);
- break;
- case TCP_NEWMESSAGE:
- tm2 = new_tcpmessage(tm->header.mn_ReplyPort);
- if (!tm2) {
- tm->result = false;
- tm->error = ERROR_OOM;
- ReplyMsg(&tm->header);
- break;
- }
-
- tm2->ident = tm->ident;
-
- tm->data = tm2;
- tm->result = true;
- tm->error = NO_ERROR;
-
- ReplyMsg(&tm->header);
- break;
- case TCP_DISPOSE:
- free_tcpmessage(tm);
- break;
- case TCP_DIE:
- /* we have to assume they've done the right thing and
- disposed of all messages (closing all connections) */
- /* all we have to do is to free whatever is left over
- and exit */
-
- death_port = tm->header.mn_ReplyPort;
-
- free_tcpmessage(tm);
-
- if (SocketBase) {
- CloseLibrary(SocketBase);
- SocketBase = nil;
- }
-
- while (tm = (tcpmessage *)RemHead(&waiting)) {
- if (tm->ident && tm->command != TCP_CONNECT)
- free_tcpident(tm->ident);
- free_tcpmessage(tm);
- }
-
- /* perhaps should do a little more ... */
-
- return death_port;
- case TCP_PEERNAME:
- if (!SocketBase) { /* someone is yankin' our chain */
- tm->result = false;
- tm->error = ERROR_NO_CONNECTION;
-
- ReplyMsg(&tm->header);
- break;
- }
- tcp_peername(SocketBase, tm);
- break;
- case TCP_SERVICE:
- if (!SocketBase) {
- SocketBase = OpenLibrary("bsdsocket.library", 0);
- if (!SocketBase) {
- tm->result = false;
- tm->error = ERROR_NO_CONNECTION;
-
- ReplyMsg(&tm->header);
- break;
- }
- }
- tcp_service(SocketBase, tm);
- if (IsListEmpty(&waiting)) {
- /* absolutely everything has closed. We can close
- the socket library now in safety */
- CloseLibrary(SocketBase);
- SocketBase = nil;
- }
- break;
- default:
- tm->result = false;
- tm->error = ERROR_UNKNOWN_COMMAND;
- ReplyMsg(&tm->header);
- break;
- }
- }
-
- if (n < 0) { /* hmmm ... this seems to happen when they close the library underneath us */
- /* should send back any waiting reads/writes/listens */
- CloseLibrary(SocketBase);
- SocketBase = nil;
-
- continue;
- }
-
- #ifdef SDLFKJ
- truth (n >= 0); /* I don't _think_ we should ever get SIGINTR ... */
- #endif
-
- if (n <= 0) continue; /* no fds are ready, so don't look through list */
-
- for (tm = (tcpmessage *)waiting.lh_Head;
- tm2 = (tcpmessage *)tm->header.mn_Node.ln_Succ;
- tm = tm2) {
-
- switch (tm->command) {
- case TCP_LISTEN:
- ti = tm->ident;
- verify(ti, V_tcpident);
-
- if (FD_ISSET(ti->fd, &read_tmp)) { /* ready to accept */
- tcp_accept(SocketBase, tm, &waiting, commands);
- }
- break;
- case TCP_READ:
- ti = tm->ident;
- verify(ti, V_tcpident);
-
- if (FD_ISSET(ti->fd, &read_tmp)) { /* ready to continue reading */
- tcp_read_more(SocketBase, tm, &waiting, &read_set, &max_fd);
- }
- break;
- case TCP_WRITE:
- ti = tm->ident;
- verify(ti, V_tcpident);
-
- if (FD_ISSET(ti->fd, &write_tmp)) { /* ready to continue writing */
- tcp_write_more(SocketBase, tm, &waiting, &write_set, &max_fd);
- }
- break;
- }
- }
- }
- }
-
- void __saveds __asm tcp_handler(register __a0 b8 *parent_port)
- {
- struct MsgPort *mp, *tcp_commands;
- tcpmessage *tm, *new_tm;
-
- mem_tracking_on();
-
- mp = FindPort(parent_port);
- if (!mp) {
- truth(FindPort(parent_port) != nil); /* will display an alert */
- return; /* OOOPS - not overly much we can do here */
- }
-
- tcp_commands = CreatePort(0, 0);
- if (!tcp_commands) {
- /* this is a signal jobbie because we can't send a message
- so when they wait for the signal, and then can't GetMsg
- they should guess that something has gone wrong */
- Signal(mp->mp_SigTask, 1 << mp->mp_SigBit);
- return;
- }
-
- tm = new_tcpmessage(tcp_commands);
- if (!tm) {
- /* again ... our message doesn't exist, so just signal */
- Signal(mp->mp_SigTask, 1 << mp->mp_SigBit);
- DeletePort(tcp_commands);
- return;
- }
-
- new_tm = new_tcpmessage(tcp_commands);
- if (!new_tm) {
- Signal(mp->mp_SigTask, 1 << mp->mp_SigBit);
- DeletePort(tcp_commands);
- free_tcpmessage(tm);
- return;
- }
-
- new_tm->command = TCP_NEWMESSAGE;
- new_tm->header.mn_ReplyPort = mp;
- /* just a guess ... if they want something different, they'se gonna haveta do it themselves */
-
- /* ok, we have enough to communicate ... tell them we started ok */
-
- tm->command = TCP_STARTUP;
- tm->data = tcp_commands; /* this tells them where to send commands */
- tm->result = true; /* not necessary but wth */
-
- PutMsg(mp, &tm->header);
-
- /* wait for it to come back */
-
- Wait(1 << tcp_commands->mp_SigBit);
- GetMsg(tcp_commands); /* should be guaranteed to be tm back ;) */
-
- /* ok, time to carry on */
- /* send them a message to bootstrap their message system */
-
- PutMsg(mp, &new_tm->header);
-
- /* Reusing mp here for the death port */
-
- mp = running_running(tm, tcp_commands);
-
- DeletePort(tcp_commands);
- free_tcpmessage(tm);
-
- check_memory();
-
- Forbid(); /* once we are RemTask'd the Forbid will be lifted */
-
- Signal(mp->mp_SigTask, 1 << mp->mp_SigBit); /* signal parent we are dead */
-
- return;
- }
-