diff options
author | Joel Sherrill <joel.sherrill@OARcorp.com> | 1998-08-19 21:32:28 +0000 |
---|---|---|
committer | Joel Sherrill <joel.sherrill@OARcorp.com> | 1998-08-19 21:32:28 +0000 |
commit | 39e6e65a2c5a3312f365d59f23c469641e049c82 (patch) | |
tree | c0d6000a18918db140589a84596a8dfc215a4ced /c/src/lib/libnetworking/lib | |
parent | First version produced. (diff) | |
download | rtems-39e6e65a2c5a3312f365d59f23c469641e049c82.tar.bz2 |
Base files
Diffstat (limited to 'c/src/lib/libnetworking/lib')
-rw-r--r-- | c/src/lib/libnetworking/lib/README | 1 | ||||
-rw-r--r-- | c/src/lib/libnetworking/lib/getprotoby.c | 42 | ||||
-rw-r--r-- | c/src/lib/libnetworking/lib/syslog.c | 187 | ||||
-rw-r--r-- | c/src/lib/libnetworking/lib/tftpDriver.c | 609 |
4 files changed, 839 insertions, 0 deletions
diff --git a/c/src/lib/libnetworking/lib/README b/c/src/lib/libnetworking/lib/README new file mode 100644 index 0000000000..787c24c012 --- /dev/null +++ b/c/src/lib/libnetworking/lib/README @@ -0,0 +1 @@ +Sources from application-level (as opposed to kernel-level) libraries. diff --git a/c/src/lib/libnetworking/lib/getprotoby.c b/c/src/lib/libnetworking/lib/getprotoby.c new file mode 100644 index 0000000000..b90c247a54 --- /dev/null +++ b/c/src/lib/libnetworking/lib/getprotoby.c @@ -0,0 +1,42 @@ +#include <netdb.h> +#include <string.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> + +static const struct protoent prototab[] = { + { "ip", NULL, IPPROTO_IP }, + { "icmp", NULL, IPPROTO_ICMP }, + { "tcp", NULL, IPPROTO_TCP }, + { "udp", NULL, IPPROTO_UDP }, + }; + +/* + * Dummy version of BSD getprotobyname() + */ +struct protoent * +getprotobyname (const char *name) +{ + int i; + + for (i = 0 ; i < (sizeof prototab / sizeof prototab[0]) ; i++) { + if (strcmp (name, prototab[i].p_name) == 0) + return &prototab[i]; + } + return NULL; +} + +/* + * Dummy version of BSD getprotobynumber() + */ +struct protoent * +getprotobynumber (int proto) +{ + int i; + + for (i = 0 ; i < (sizeof prototab / sizeof prototab[0]) ; i++) { + if (proto == prototab[i].p_proto) + return &prototab[i]; + } + return NULL; +} diff --git a/c/src/lib/libnetworking/lib/syslog.c b/c/src/lib/libnetworking/lib/syslog.c new file mode 100644 index 0000000000..7f468c81e6 --- /dev/null +++ b/c/src/lib/libnetworking/lib/syslog.c @@ -0,0 +1,187 @@ +/* + * RTEMS version of syslog and associated routines + * + * $Id$ + */ + +#include <rtems.h> +#include <stdio.h> +#include <stdarg.h> +#include <errno.h> +#include <syslog.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> + +static int LogStatus = LOG_CONS; +static const char *LogTag = "syslog"; +static int LogFacility = LOG_USER; +static int LogMask = 0xff; + +static int LogFd = -1; +static rtems_id LogSemaphore; +extern struct in_addr rtems_bsdnet_log_host_address; + +#define SYSLOG_PORT 514 + +void +syslog (int pri, const char *fmt, ...) +{ + va_list ap; + + va_start (ap, fmt); + vsyslog (pri, fmt, ap); + va_end (ap); +} + +/* + * FIXME: Should cbuf be static? It could be if we put the mutex + * around the entire body of this routine. Then we wouldn't + * have to worry about blowing stacks with a local variable + * that large. Could make cbuf bigger, too. + */ +void +vsyslog (int pri, const char *fmt, va_list ap) +{ + int cnt; + char *cp; + char *msgp, cbuf[200]; + int sent; + + if (pri & ~(LOG_PRIMASK|LOG_FACMASK)) { + syslog (LOG_ERR|LOG_CONS|LOG_PERROR|LOG_PID, + "syslog: unknown facility/priority: %#x", pri); + pri &= LOG_PRIMASK|LOG_FACMASK; + } + + if (!LOG_MASK(LOG_PRI(pri)) & LogMask) + return; + + if ((pri & LOG_FACMASK) == 0) + pri |= LogFacility; + + cnt = sprintf (cbuf, "<%d>", pri); + cp = msgp = cbuf + cnt; + if (LogTag) { + const char *lp = LogTag; + while ((*cp = *lp++) != '\0') + cp++; + } + if (LogStatus & LOG_PID) { + rtems_id tid; + rtems_task_ident (RTEMS_SELF, 0, &tid); + cnt = sprintf (cp, "[%#lx]", (unsigned long)tid); + cp += cnt; + } + if (LogTag) { + *cp++ = ':'; + *cp++ = ' '; + } + cnt = vsprintf (cp, fmt, ap); + cnt += cp - cbuf; + if (cbuf[cnt-1] == '\n') + cbuf[--cnt] = '\0'; + + if (LogStatus & LOG_PERROR) + printf ("%s\n", cbuf); + + /* + * Grab the mutex + */ + sent = 0; + if ((rtems_bsdnet_log_host_address.s_addr != INADDR_ANY) + && (LogFd >= 0) + && (rtems_semaphore_obtain (LogSemaphore, RTEMS_WAIT, RTEMS_NO_TIMEOUT) == RTEMS_SUCCESSFUL)) { + /* + * Set the destination address/port + */ + struct sockaddr_in farAddress; + farAddress.sin_family = AF_INET; + farAddress.sin_port = htons (SYSLOG_PORT); + farAddress.sin_addr = rtems_bsdnet_log_host_address; + memset (farAddress.sin_zero, '\0', sizeof farAddress.sin_zero); + + /* + * Send the message + */ + if (sendto (LogFd, cbuf, cnt, 0, (struct sockaddr *)&farAddress, sizeof farAddress) >= 0) + sent = 1; + rtems_semaphore_release (LogSemaphore); + } + if (!sent && (LogStatus & LOG_CONS) && !(LogStatus & LOG_PERROR)) + printf ("%s\n", msgp); +} + +void +openlog (const char *ident, int logstat, int logfac) +{ + rtems_status_code sc; + struct sockaddr_in myAddress; + + if (ident != NULL) + LogTag = ident; + LogStatus = logstat; + if (logfac != 0 && (logfac & ~LOG_FACMASK) == 0) + LogFacility = logfac; + + /* + * Create the socket + */ + if ((LogFd = socket (AF_INET, SOCK_DGRAM, 0)) < 0) { + printf ("Can't create syslog socket: %d\n", errno); + return; + } + + /* + * Bind socket to name + */ + myAddress.sin_family = AF_INET; + myAddress.sin_addr.s_addr = INADDR_ANY; + myAddress.sin_port = 0; + memset (myAddress.sin_zero, '\0', sizeof myAddress.sin_zero); + if (bind (LogFd, (struct sockaddr *)&myAddress, sizeof (myAddress)) < 0) { + close (LogFd); + LogFd = -1; + printf ("Can't bind syslog socket: %d\n", errno); + return; + } + + /* + * Create the mutex + */ + sc = rtems_semaphore_create (rtems_build_name('s', 'L', 'o', 'g'), + 1, + RTEMS_PRIORITY | + RTEMS_BINARY_SEMAPHORE | + RTEMS_INHERIT_PRIORITY | + RTEMS_NO_PRIORITY_CEILING | + RTEMS_LOCAL, + 0, + &LogSemaphore); + if (sc != RTEMS_SUCCESSFUL) { + printf ("Can't create syslog seamphore: %d\n", sc); + close (LogFd); + LogFd = -1; + } +} + +void +closelog(void) +{ + if (LogFd >= 0) { + close (LogFd); + LogFd = -1; + rtems_semaphore_delete (LogSemaphore); + } +} + +int +setlogmask (int pmask) +{ + int omask; + + omask = LogMask; + if (pmask != 0) + LogMask = pmask; + return (omask); +} diff --git a/c/src/lib/libnetworking/lib/tftpDriver.c b/c/src/lib/libnetworking/lib/tftpDriver.c new file mode 100644 index 0000000000..cf75623580 --- /dev/null +++ b/c/src/lib/libnetworking/lib/tftpDriver.c @@ -0,0 +1,609 @@ +/* + * Trivial File Transfer Protocol (RFC 1350) + * + * Transfer file to/from remote host + * + * W. Eric Norum + * Saskatchewan Accelerator Laboratory + * University of Saskatchewan + * Saskatoon, Saskatchewan, CANADA + * eric@skatter.usask.ca + * + * $Id$ + */ + +#include <stdio.h> +#include <errno.h> +#include <malloc.h> +#include <string.h> +#include <unistd.h> +#include <rtems.h> +#include <rtems/libio.h> +#include <rtems/rtems_bsdnet.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +/* + * Range of UDP ports to try + */ +#define UDP_PORT_BASE 3180 + +/* + * Pathname prefix + */ +#define TFTP_PATHNAME_PREFIX "/TFTP/" + +/* + * Default limits + */ +#define PACKET_REPLY_MILLISECONDS 6000 +#define OPEN_RETRY_LIMIT 10 +#define IO_RETRY_LIMIT 10 + +/* + * TFTP opcodes + */ +#define TFTP_OPCODE_RRQ 1 +#define TFTP_OPCODE_WRQ 2 +#define TFTP_OPCODE_DATA 3 +#define TFTP_OPCODE_ACK 4 +#define TFTP_OPCODE_ERROR 5 + +/* + * Largest data transfer + */ +#define TFTP_BUFSIZE 512 + +/* + * Packets transferred between machines + */ +union tftpPacket { + /* + * RRQ/WRQ packet + */ + struct tftpRWRQ { + rtems_unsigned16 opcode; + char filename_mode[TFTP_BUFSIZE]; + } tftpRWRQ; + + /* + * DATA packet + */ + struct tftpDATA { + rtems_unsigned16 opcode; + rtems_unsigned16 blocknum; + rtems_unsigned8 data[TFTP_BUFSIZE]; + } tftpDATA; + + /* + * ACK packet + */ + struct tftpACK { + rtems_unsigned16 opcode; + rtems_unsigned16 blocknum; + } tftpACK; + + /* + * ERROR packet + */ + struct tftpERROR { + rtems_unsigned16 opcode; + rtems_unsigned16 errorCode; + char errorMessage[TFTP_BUFSIZE]; + } tftpERROR; +}; + +/* + * State of each TFTP stream + */ +struct tftpStream { + /* + * Buffer for storing most recently-received packet + */ + union tftpPacket pkbuf; + + /* + * Last block number received + */ + rtems_unsigned16 blocknum; + + /* + * Data transfer socket + */ + int socket; + struct sockaddr_in myAddress; + struct sockaddr_in farAddress; + + /* + * Indices into buffer + */ + int nleft; + int nused; + + /* + * Flags + */ + int firstReply; + int eof; +}; + +/* + * Number of streams open at the same time + */ +static rtems_id tftp_mutex; +static int nStreams; +static struct tftpStream ** volatile tftpStreams; + +/* + * Initialize the TFTP driver + */ +rtems_device_driver rtems_tftp_initialize( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *pargp +) +{ + rtems_status_code sc; + + sc = rtems_semaphore_create (rtems_build_name('T', 'F', 'T', 'P'), + 1, + RTEMS_FIFO | + RTEMS_BINARY_SEMAPHORE | + RTEMS_NO_INHERIT_PRIORITY | + RTEMS_NO_PRIORITY_CEILING | + RTEMS_LOCAL, + 0, + &tftp_mutex); + if (sc != RTEMS_SUCCESSFUL) + return sc; + rtems_io_register_name (TFTP_PATHNAME_PREFIX, major, minor); + return RTEMS_SUCCESSFUL; +} + +/* + * Set error message + * This RTEMS/UNIX error mapping needs to be fixed! + */ +static void +tftpSetErrno (struct tftpStream *tp) +{ + unsigned int tftpError; + static const int errorMap[] = { + 0, + ENOENT, + EPERM, + ENOSPC, + EINVAL, + ENXIO, + EEXIST, + ESRCH, + 0, + }; + + tftpError = ntohs (tp->pkbuf.tftpERROR.errorCode); + if (tftpError < (sizeof errorMap / sizeof errorMap[0])) + errno = errorMap[tftpError]; + else + errno = 1000 + tftpError; +} + +/* + * Send a message to make the other end shut up + */ +static void +sendStifle (struct tftpStream *tp, struct sockaddr_in *to) +{ + int len; + + /* + * Create the error packet (Unknown transfer ID). + */ + tp->pkbuf.tftpERROR.opcode = htons (TFTP_OPCODE_ERROR); + tp->pkbuf.tftpERROR.errorCode = htons (5); + len = sizeof tp->pkbuf.tftpERROR.opcode + + sizeof tp->pkbuf.tftpERROR.errorCode + 1; + len += sprintf (tp->pkbuf.tftpERROR.errorMessage, "GO AWAY"); + + /* + * Send it + */ + sendto (tp->socket, (char *)&tp->pkbuf, len, 0, + (struct sockaddr *)to, sizeof *to); +} + +/* + * Wait for a data packet + */ +static int +getPacket (struct tftpStream *tp) +{ + int len; + struct timeval tv; + + tv.tv_sec = 6; + tv.tv_usec = 0; + setsockopt (tp->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv); + for (;;) { + union { + struct sockaddr s; + struct sockaddr_in i; + } from; + int fromlen = sizeof from; + len = recvfrom (tp->socket, (char *)&tp->pkbuf, + sizeof tp->pkbuf, 0, + &from.s, &fromlen); + if (len < 0) + break; + if (from.i.sin_addr.s_addr == tp->farAddress.sin_addr.s_addr) { + if (tp->firstReply) { + tp->firstReply = 0; + tp->farAddress.sin_port = from.i.sin_port; + } + if (tp->farAddress.sin_port == from.i.sin_port) + break; + } + + /* + * Packet is from someone with whom we are + * not interested. Tell them to go away. + */ + sendStifle (tp, &from.i); + } + tv.tv_sec = 0; + setsockopt (tp->socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof tv); + return len; +} + +/* + * Send an acknowledgement + */ +static int +sendAck (struct tftpStream *tp) +{ + /* + * Create the acknowledgement + */ + tp->pkbuf.tftpACK.opcode = htons (TFTP_OPCODE_ACK); + tp->pkbuf.tftpACK.blocknum = htons (tp->blocknum); + + /* + * Send it + */ + if (sendto (tp->socket, (char *)&tp->pkbuf, sizeof tp->pkbuf.tftpACK, 0, + (struct sockaddr *)&tp->farAddress, + sizeof tp->farAddress) < 0) + return errno; + return 0; +} + +/* + * Release a stream and clear the pointer to it + */ +static void +releaseStream (int s) +{ + rtems_semaphore_obtain (tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + free (tftpStreams[s]); + tftpStreams[s] = NULL; + rtems_semaphore_release (tftp_mutex); +} + +/* + * Open a TFTP stream + */ +rtems_device_driver rtems_tftp_open( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *pargp +) +{ + rtems_libio_open_close_args_t *ap = pargp; + struct tftpStream *tp; + int retryCount; + rtems_unsigned32 farAddress; + int s; + int len; + char *cp1, *cp2; + char *remoteFilename; + rtems_interval now; + rtems_status_code sc; + + /* + * Read-only for now + */ + if (ap->flags & LIBIO_FLAGS_WRITE) + return RTEMS_NOT_IMPLEMENTED; + + /* + * Pick apart the name into a host:pathname pair + */ + if (strlen (ap->iop->pathname) <= strlen (TFTP_PATHNAME_PREFIX)) + return RTEMS_INVALID_NAME; + cp2 = ap->iop->pathname + strlen (TFTP_PATHNAME_PREFIX); + if (*cp2 == '/') { + farAddress = rtems_bsdnet_bootp_server_address.s_addr; + } + else { + char *hostname; + + cp1 = cp2; + while (*cp2 != '/') { + if (*cp2 == '\0') + return RTEMS_INVALID_NAME; + cp2++; + } + len = cp2 - cp1; + hostname = malloc (len + 1); + if (hostname == NULL) + return RTEMS_NO_MEMORY; + strncpy (hostname, cp1, len); + hostname[len] = '\0'; + farAddress = inet_addr (hostname); + free (hostname); + } + if ((farAddress == 0) || (farAddress == ~0)) + return RTEMS_INVALID_NAME; + if (*++cp2 == '\0') + return RTEMS_INVALID_NAME; + remoteFilename = cp2; + if (strlen (remoteFilename) > (TFTP_BUFSIZE - 10)) + return RTEMS_INVALID_NAME; + + /* + * Find a free stream + */ + sc = rtems_semaphore_obtain (tftp_mutex, RTEMS_WAIT, RTEMS_NO_TIMEOUT); + if (sc != RTEMS_SUCCESSFUL) + return sc; + for (s = 0 ; s < nStreams ; s++) { + if (tftpStreams[s] == NULL) + break; + } + if (s == nStreams) { + /* + * Reallocate stream pointers + * Guard against the case where realloc() returns NULL. + */ + struct tftpStream **np; + + np = realloc (tftpStreams, ++nStreams * sizeof *tftpStreams); + if (np == NULL) { + rtems_semaphore_release (tftp_mutex); + return RTEMS_NO_MEMORY; + } + tftpStreams = np; + } + tp = tftpStreams[s] = malloc (sizeof (struct tftpStream)); + rtems_semaphore_release (tftp_mutex); + if (tp == NULL) + return RTEMS_NO_MEMORY; + ap->iop->data0 = s; + ap->iop->data1 = tp; + + /* + * Create the socket + */ + if ((tp->socket = socket (AF_INET, SOCK_DGRAM, 0)) < 0) { + releaseStream (s); + return RTEMS_TOO_MANY; + } + + /* + * Bind the socket to a local address + */ + retryCount = 0; + rtems_clock_get (RTEMS_CLOCK_GET_TICKS_SINCE_BOOT, &now); + for (;;) { + int try = (now + retryCount) % 10; + + tp->myAddress.sin_family = AF_INET; + tp->myAddress.sin_port = htons (UDP_PORT_BASE + nStreams * try + minor); + tp->myAddress.sin_addr.s_addr = htonl (INADDR_ANY); + if (bind (tp->socket, (struct sockaddr *)&tp->myAddress, sizeof tp->myAddress) >= 0) + break; + if (++retryCount == 10) { + close (tp->socket); + releaseStream (minor); + return RTEMS_RESOURCE_IN_USE; + } + } + + /* + * Set the UDP destination to the TFTP server + * port on the remote machine. + */ + tp->farAddress.sin_family = AF_INET; + tp->farAddress.sin_addr.s_addr = farAddress; + tp->farAddress.sin_port = htons (69); + + /* + * Start the transfer + */ + tp->firstReply = 1; + for (;;) { + /* + * Create the request + */ + tp->pkbuf.tftpRWRQ.opcode = htons (TFTP_OPCODE_RRQ); + cp1 = tp->pkbuf.tftpRWRQ.filename_mode; + cp2 = remoteFilename; + while ((*cp1++ = *cp2++) != '\0') + continue; + cp2 = "octet"; + while ((*cp1++ = *cp2++) != '\0') + continue; + len = cp1 - (char *)&tp->pkbuf.tftpRWRQ; + + /* + * Send the request + */ + if (sendto (tp->socket, (char *)&tp->pkbuf, len, 0, + (struct sockaddr *)&tp->farAddress, + sizeof tp->farAddress) < 0) { + close (tp->socket); + releaseStream (minor); + return RTEMS_UNSATISFIED; + } + + /* + * Get reply + */ + len = getPacket (tp); + if (len >= (int) sizeof tp->pkbuf.tftpACK) { + int opcode = ntohs (tp->pkbuf.tftpDATA.opcode); + if ((opcode == TFTP_OPCODE_DATA) + && (ntohs (tp->pkbuf.tftpDATA.blocknum) == 1)) { + tp->nused = 0; + tp->blocknum = 1; + tp->nleft = len - 2 * sizeof (rtems_unsigned16); + tp->eof = (tp->nleft < TFTP_BUFSIZE); + if (sendAck (tp) != 0) { + close (tp->socket); + releaseStream (minor); + return RTEMS_UNSATISFIED; + } + break; + } + if (opcode == TFTP_OPCODE_ERROR) { + tftpSetErrno (tp); + close (tp->socket); + releaseStream (ap->iop->data0); + return RTEMS_INTERNAL_ERROR; + } + } + + /* + * Keep trying + */ + if (++retryCount >= OPEN_RETRY_LIMIT) { + close (tp->socket); + releaseStream (minor); + return RTEMS_UNSATISFIED; + } + } + return RTEMS_SUCCESSFUL; +} + +/* + * Read from a TFTP stream + */ +rtems_device_driver rtems_tftp_read( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *pargp +) +{ + rtems_libio_rw_args_t *ap = pargp; + char *bp; + struct tftpStream *tp; + int retryCount; + int nwant; + + tp = ap->iop->data1; + + /* + * Read till user request is satisfied or EOF is reached + */ + bp = ap->buffer; + nwant = ap->count; + while (nwant) { + if (tp->nleft) { + int count; + if (nwant < tp->nleft) + count = nwant; + else + count = tp->nleft; + memcpy (bp, &tp->pkbuf.tftpDATA.data[tp->nused], count); + tp->nused += count; + tp->nleft -= count; + bp += count; + nwant -= count; + if (nwant == 0) + break; + } + if (tp->eof) + break; + + /* + * Wait for the next packet + */ + retryCount = 0; + for (;;) { + int len = getPacket (tp); + if (len >= (int)sizeof tp->pkbuf.tftpACK) { + int opcode = ntohs (tp->pkbuf.tftpDATA.opcode); + rtems_unsigned16 nextBlock = tp->blocknum + 1; + if ((opcode == TFTP_OPCODE_DATA) + && (ntohs (tp->pkbuf.tftpDATA.blocknum) == nextBlock)) { + tp->nused = 0; + tp->nleft = len - 2 * sizeof (rtems_unsigned16); + tp->eof = (tp->nleft < TFTP_BUFSIZE); + tp->blocknum++; + if (sendAck (tp) != 0) + return RTEMS_IO_ERROR; + break; + } + if (opcode == TFTP_OPCODE_ERROR) { + tftpSetErrno (tp); + return RTEMS_INTERNAL_ERROR; + } + } + + /* + * Keep trying? + */ + if (++retryCount == IO_RETRY_LIMIT) + return RTEMS_IO_ERROR; + if (sendAck (tp) != 0) + return RTEMS_IO_ERROR; + } + } + ap->bytes_moved = ap->count - nwant; + return RTEMS_SUCCESSFUL; +} + +/* + * Close a TFTP stream + */ +rtems_device_driver rtems_tftp_close( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *pargp +) +{ + rtems_libio_open_close_args_t *ap = pargp; + struct tftpStream *tp = ap->iop->data1;; + + if (!tp->eof && !tp->firstReply) { + /* + * Tell the other end to stop + */ + rtems_interval ticksPerSecond; + sendStifle (tp, &tp->farAddress); + rtems_clock_get (RTEMS_CLOCK_GET_TICKS_PER_SECOND, &ticksPerSecond); + rtems_task_wake_after (1 + ticksPerSecond / 10); + } + close (tp->socket); + releaseStream (ap->iop->data0); + return RTEMS_SUCCESSFUL; +} + +rtems_device_driver rtems_tftp_write( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *pargp +) +{ + return RTEMS_NOT_CONFIGURED; +} + +rtems_device_driver rtems_tftp_control( + rtems_device_major_number major, + rtems_device_minor_number minor, + void *pargp +) +{ + return RTEMS_NOT_CONFIGURED; +} |