Commit 997161ef authored by Per Cederqvist's avatar Per Cederqvist

Initial revision

parent febece4e
/*
** tcp.c A small ISC TCP/IP subsystem demo program
**
** Copyright (c) 1992 Peter Eriksson and Per Cederqvist of the
** Lysator Academic Computer Association.
**
** history:
** 910306 pen initial coding
** 920211 pen cleaned up
*/
#include <stdio.h>
#include <errno.h>
/*
** Could do a "#define ISC_UDGTYPE foobar" to change the type of
** the (IscSession) 'udg' member pointer type from "void *" to "foobar *".
*/
#include <isc.h>
extern void abort(void);
static IscConfig cfg =
{
1005,
{
1001,
{ -1, -1, -1, -1, -1 }
},
{
1001,
{ NULL, NULL, NULL },
NULL
}
};
#define TIMEOUT 30000 /* 30 seconds */
Perror(char *msg)
{
perror(msg);
exit(1);
}
void
main(void)
{
IscMaster * mcb; /* ISC master control block */
IscSession * scb; /* ISC session control block */
IscEvent * ecb; /* ISC event control block */
puts("Beginning operation...");
/*
** Initialize the ISC subsystem, create a Master Control Block
*/
if ((mcb = isc_initialize(&cfg)) == NULL)
Perror("isc_initialize");
puts("ISC initialized, connecting to remote service...");
/*
** Establish a TCP session to a remote service (IRC)
*/
if ((scb = isc_opentcp(mcb, "obel5.ida.liu.se", "2000")) == NULL)
Perror("isc_opentcp");
puts("Connection initiated, enabling listening ports...");
/*
** Enable remote connection attempts at port #20000 and #20001
*/
if (isc_listentcp(mcb, "localhost", "20000") == NULL)
Perror("isc_listentcp-20000");
if (isc_listentcp(mcb, NULL, "20001") == NULL)
Perror("isc_listentcp-20001");
puts("Listening ports installed, entering main event loop...");
/*
** Handle all events
*/
while (ecb = isc_getnextevent(mcb, TIMEOUT))
{
switch (ecb->event)
{
case ISC_EVENT_CONNECTED:
puts("Server connection established.");
break;
case ISC_EVENT_REJECTED:
puts("Server connection rejected.");
isc_destroy(mcb, scb);
scb = NULL;
break;
case ISC_EVENT_ERROR: /* ISC subsystem error */
fprintf(stderr, "ISC-ERROR: errno = %d", errno);
if (ecb->msg)
fprintf(stderr, " (%s)", ecb->msg->buffer);
putc('\n', stderr);
/*
** Should probably do something more sensible than
** just finishing off the session
*/
if (ecb->session)
isc_destroy(mcb, ecb->session);
break;
case ISC_EVENT_TIMEOUT: /* Timeout limit reached */
puts("*** Timeout ***");
break;
case ISC_EVENT_LOGIN: /* New client requesting connection */
{
char laddr[256];
char lserv[256];
char raddr[256];
char rserv[256];
strcpy(raddr, "<unknown>");
strcpy(rserv, "<unknown>");
strcpy(laddr, "<unknown>");
strcpy(lserv, "<unknown>");
fetch_address(ecb->session->info.tcp.raddr,
raddr,
sizeof(raddr)-1,
rserv,
sizeof(rserv)-1);
fetch_address(ecb->session->info.tcp.laddr,
laddr,
sizeof(laddr)-1,
lserv,
sizeof(lserv)-1);
printf("New session #%d on %s/%s connecting from: %s/%s\n",
ecb->session,
laddr, lserv,
raddr, rserv);
/*
** Here we could assign the 'ecb->session->udg' member some
** arbitrary value to be used later by our own routines
*/
}
break;
case ISC_EVENT_LOGOUT: /* Client closed connection */
if (ecb->session == scb)
printf("Server disconnecting\n");
else
printf("Client #%d disconnecting\n", ecb->session);
/*
** Deallocate the storage reserved for the session
*/
isc_destroy(mcb, ecb->session);
break;
case ISC_EVENT_MESSAGE: /* Message from client or server */
if (ecb->session == scb)
printf("Message from server: '%s'\n", ecb->msg->buffer);
else
{
printf("Message from client #%d: '%s'\n",
ecb->session,
ecb->msg->buffer);
/*
** Reply with some text to the client
*/
isc_write(ecb->session, "OK, got it\n\r", 12);
isc_flush(ecb->session);
}
break;
default: /* NOTREACHED - OR SHOULDN'T BE ATLEAST :-) */
Perror("NOTREACHED");
}
/*
** Finished with the event, lets get rid of it
*/
isc_dispose(ecb);
}
/* NOTREACHED - I THINK ... */
Perror("SHUTDOWN");
}
const char *strerror(int num)
{
static char foo[256];
sprintf(foo, "Error #%d", num);
return foo;
}
fetch_address(IscAddress *ia,
char *host,
int hlen,
char *port,
int plen)
{
char *cp;
cp = isc_gethostname(ia, host, hlen);
if (!cp)
isc_getipnum(ia, host, hlen);
cp = isc_getservice(ia, port, plen, ISC_TYPE_TCP);
if (!cp)
sprintf(port, "%d", isc_getportnum(ia));
}
/*
** intern.h Definitions and prototypes used internally
**
** Copyright (c) 1991 Peter Eriksson and Per Cederqvist of the
** Lysator Academic Computer Association.
**
** history:
** 910305 pen initial coding
*/
#ifndef __ISC_INTERNALS_H__
#define __ISC_INTERNALS_H__
/* isc_log.c */
extern void
isc_log(const char *format, ...);
/* isc_abort.c */
extern void
isc_abort(const char *message);
/* isc_alloc.c */
extern void *
isc_malloc(size_t length);
extern void *
isc_realloc(void *buf, size_t length);
extern void
isc_free(void *buf);
extern char *
isc_strdup(const char *str);
/* isc_queue.c */
extern IscMsgQueue *
isc_newqueue(void);
extern int
isc_pollqueue(IscMsgQueue *queue);
extern void
isc_killqueue(IscMsgQueue *queue);
extern void
isc_pushqueue(IscMsgQueue *queue, IscMessage *msg);
extern IscMessage *
isc_topqueue(IscMsgQueue *queue);
extern int
isc_sizequeue(IscMsgQueue *queue);
extern IscMessage *
isc_popqueue(IscMsgQueue *queue);
extern IscMessage *
isc_default_read_fn(IscHandlerList *hl, IscSession *scb);
extern int
isc_default_write_fn(IscHandlerList *hl, IscSession *scb, IscMessage *msg);
extern int
isc_default_close_fn(IscHandlerList *hl, IscSession *scb);
extern IscAddress *
isc_mkipaddress(struct sockaddr *addr);
extern void
isc_oflush(IscSession * scb);
/*
** Setup a set of functions to handle memory allocation
*/
extern void
isc_setallocfn(void * (*mallocfn)(size_t size),
void * (*reallocfn)(void * buf, size_t size),
void (*freefn)(void * buf));
/*
** Setup a function to handle fatal abort requests
*/
extern void
isc_setabortfn(void (*abortfn)(const char * msg));
#define ISC_XNEW(var) (var = isc_malloc(sizeof(*var)))
#define ISC_SCALLFUN1(SCB, NAME, ARG) \
(*SCB->fun.NAME->hcb->NAME)(SCB->fun.NAME, ARG)
#define ISC_SCALLFUN2(SCB, NAME, A1, A2) \
(*SCB->fun.NAME->hcb->NAME)(SCB->fun.NAME, A1, A2)
#define ISC_SCALLFUN3(SCB, NAME, A1, A2, A3) \
(*SCB->fun.NAME->hcb->NAME)(SCB->fun.NAME, A1, A2, A3)
#endif
/*
** isc.h structures and defines used in a ISC server
**
** Copyright (c) 1991 Peter Eriksson and Per Cederqvist of the
** Lysator Academic Computer Association.
**
** history:
** 910306 pen major overhaul
** 910307 pen type name changes, changes in structs..
** 920207 pen updated
*/
#ifndef __ISC_H__
#define __ISC_H__
#include <time.h>
#include <stdarg.h>
#include <sys/types.h>
#include <sys/socket.h>
/*
** Give the poor user a chance to change it
*/
#ifndef ISC_UDGTYPE
# define ISC_UDGTYPE void
#endif
/*
** Some type names (typedef? What is that? :-)
*/
#define IscSessionConfig struct isc_session_cfg
#define IscMasterConfig struct isc_master_config
#define IscConfig struct isc_config
#define IscAddress union isc_address
#define IscMessage struct isc_msg
#define IscMsgQE struct isc_msg_q_entry
#define IscMsgQueue struct isc_msgqueue
#define IscSessionInfo union isc_session_info
#define IscSessionStats struct isc_session_stats
#define IscHandler struct isc_handler
#define IscHandlerList struct isc_handler_list
#define IscHandlerCache struct isc_handler_cache
#define IscSession struct isc_scb
#define IscSessionList struct isc_scb_entry
#define IscMaster struct isc_mcb
#define IscEvent struct isc_ecb
/*
** Some nice defaults
*/
#define ISC_DEFAULT_MAX_MSG_SIZE 2048
#define ISC_DEFAULT_MAX_QUEUED_SIZE 300
#define ISC_DEFAULT_MAX_DEQUEUE_LEN 10
#define ISC_DEFAULT_MAX_OPEN_RETRIES 10
#define ISC_DEFAULT_MAX_BACKLOG 5
/*
** Configuration structure for sessions
*/
IscSessionConfig
{
int version; /* This is version 1001 */
struct
{
int msgsize;
int queuedsize;
int dequeuelen;
int openretries;
int backlog;
} max;
};
/*
** Master configuration structure
*/
IscMasterConfig
{
int version; /* This is version 1001 */
struct
{
void * (*alloc) (size_t size);
void * (*realloc) (void *bufp, size_t size);
void (*free) (void *bufp);
} memfn;
void (*abortfn)(const char *msg);
};
/*
** Configuration structure
*/
IscConfig
{
int version; /* This is version 1005 */
IscSessionConfig session;
IscMasterConfig master;
};
/*
** Various types of events that may happen
*/
typedef enum
{
ISC_EVENT_ERROR,
ISC_EVENT_TIMEOUT,
ISC_EVENT_LOGIN,
ISC_EVENT_LOGOUT,
ISC_EVENT_MESSAGE,
ISC_EVENT_CONNECTED,
ISC_EVENT_REJECTED
} IscEventType;
/*
** Various types of sessions
*/
typedef enum
{
ISC_TYPE_UNKNOWN,
ISC_TYPE_TCP,
ISC_TYPE_PIPE,
ISC_TYPE_FILE,
ISC_TYPE_UDP
} IscSessionType;
/*
** The different session states
*/
typedef enum
{
ISC_STATE_UNKNOWN,
ISC_STATE_CONNECTING,
ISC_STATE_RUNNING,
ISC_STATE_DISABLED,
ISC_STATE_CLOSING,
ISC_STATE_LISTENING
} IscSessionState;
/*
** Message information structure
*/
IscAddress
{
struct
{
struct sockaddr saddr;
} ip;
};
/*
** Generic message type
*/
IscMessage
{
int size; /* Maximum buffer size */
int length; /* Length of used buffer */
char * buffer; /* Pointer to buffer */
IscAddress * address; /* Pointer to address */
};
/*
** Entry in a message queue
*/
IscMsgQE
{
IscMsgQE * prev;
IscMsgQE * next;
IscMessage * msg;
};
/*
** The generic message queue
*/
IscMsgQueue
{
IscMsgQE * head;
IscMsgQE * tail;
int entries;
};
/*
** Session information structures
*/
IscSessionInfo
{
struct
{
IscAddress *raddr;
IscAddress *laddr;
} tcp;
struct
{
IscAddress *raddr;
IscAddress *laddr;
} udp;
struct
{
char * pathname;
} file;
struct
{
char * pathname;
} pipe;
};
/*
** Statistical data about a session
*/
IscSessionStats
{
struct
{
long bytes;
long packets;
} rx, tx;
};
IscHandlerCache
{
IscHandlerList * read;
IscHandlerList * write;
IscHandlerList * close;
IscHandlerList * poll;
IscHandlerList * accept;
IscHandlerList * destroy;
IscHandlerList * parse;
};
/*
** Session control structure
*/
IscSession
{
int nlinks;
IscSessionType type;
IscSessionState state;
int fd;
int mode;
int errno;
IscMsgQueue * rd_msg_q;
IscMsgQueue * wr_msg_q;
IscSessionConfig * cfg;
IscSessionInfo info;
/* Cheap transmit buffer */
/* Should be dynamically allocated - into an IscMessage */
char sendbuf[2048];
int sendindex;
time_t logintime;
time_t idlesince;
IscSessionStats stats;
IscHandlerList * handlers;
IscHandlerCache fun;
ISC_UDGTYPE * udg; /* User defined garbage :-) */
};
IscHandler
{
IscMessage * (*read) (IscHandlerList *,
IscSession *scb);
int (*write) (IscHandlerList *hl,
IscSession *scb,
IscMessage *msg);
int (*close) (IscHandlerList *hl,
IscSession *scb);
int (*poll) (IscHandlerList *hl,
IscSession *scb);
IscSession * (*accept) (IscHandlerList *hl,
IscSession *scb,
IscMessage *msg);
void (*destroy) (IscHandlerList *hl,
IscSession *scb);
IscEvent * (*parse) (IscHandlerList *hl,
IscSession *scb,
IscMessage *msg);
};
IscHandlerList
{
IscHandlerList * next;
IscHandler * hcb;
IscHandlerCache old;
};
/*
** Session list entry
*/
IscSessionList
{
IscSessionList * prev;
IscSessionList * next;
IscSession * scb;
};
/*
** The Master Control Block
*/
IscMaster
{
IscSessionConfig scfg;
IscSessionList * sessions;
};
/*
** Event control structure
*/
IscEvent
{
IscEventType event;
IscSession * session;
IscMessage * msg;
};
/*
** This routine will setup the ISC subsystem
*/
extern IscMaster *
isc_initialize(IscConfig * cfg);
/*
** Shut down all sessions associated with an ISC Master Control Block
** and deallocate all storage used by the MCB.
*/
extern void
isc_shutdown(IscMaster * mcb);
/*
** Establish a TCP port to listen for connections at
*/
extern IscSession *
isc_listentcp(IscMaster * mcb,
const char * address,
const char * service);
/*
** Initiate a new session with a previously opened file