diff options
Diffstat (limited to 'usr/src/lib/print/libprint/common/network.c')
-rw-r--r-- | usr/src/lib/print/libprint/common/network.c | 418 |
1 files changed, 418 insertions, 0 deletions
diff --git a/usr/src/lib/print/libprint/common/network.c b/usr/src/lib/print/libprint/common/network.c new file mode 100644 index 0000000000..109b44a7d5 --- /dev/null +++ b/usr/src/lib/print/libprint/common/network.c @@ -0,0 +1,418 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2006 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +/*LINTLIBRARY*/ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdarg.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> +#include <sys/mman.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <errno.h> +#include <syslog.h> +#include <sys/utsname.h> + +#include <network.h> +#include <misc.h> + +static int read_wait_time_sec = 60; +static int write_wait_time_sec = 10; + +/* + * This module implements a set of functions to handle network + * communications. It attempts to hide any "uglyness" that might be + * necessary for such communications. + */ + + +/* + * null() is to be used as a signal handler that does nothing. It is used in + * place of SIG_IGN, because we want the signal to be delivered and + * interupt the current system call. + */ +static void +null(int i) +{ + syslog(LOG_DEBUG, "null(%d)", i); +} + +/* + * net_open() opens a tcp connection to the printer port on the host specified + * in the arguments passed in. If the connection is not made in the + * timeout (in seconds) passed in, an error it returned. If the host is + * unknown, an error is returned. If all is well, a file descriptor is + * returned to be used for future communications. + */ +int +net_open(char *host, int timeout) +{ + struct hostent *hp; + struct servent *sp; + struct sockaddr_in6 sin; + void (*old_handler)(); + static struct utsname uts; + + int s, + lport, + err, + error_num; + unsigned timo = 1; + + syslog(LOG_DEBUG, "net_open(%s, %d)", (host != NULL ? host : "NULL"), + timeout); + /* + * Get the host address and port number to connect to. + */ + if (host == NULL) { + return (-1); + } + + (void) memset((char *)&sin, NULL, sizeof (sin)); + if ((hp = getipnodebyname(host, AF_INET6, AI_DEFAULT, + &error_num)) == NULL) { + syslog(LOG_DEBUG|LOG_ERR, "unknown host %s " + "getipnodebyname() returned %d", host, error_num); + return (NETWORK_ERROR_HOST); + } + (void) memcpy((caddr_t)&sin.sin6_addr, hp->h_addr, hp->h_length); + sin.sin6_family = hp->h_addrtype; + freehostent(hp); + + if ((sp = getservbyname("printer", "tcp")) == NULL) { + syslog(LOG_DEBUG|LOG_ERR, "printer/tcp: unknown service"); + return (NETWORK_ERROR_SERVICE); + } + sin.sin6_port = sp->s_port; + +retry: + /* + * Try connecting to the server. + * + * Use 0 as lport means that rresvport_af() will bind to a port in + * the anonymous privileged port range. + */ + lport = 0; + s = rresvport_af(&lport, AF_INET6); + if (s < 0) + return (NETWORK_ERROR_PORT); + + old_handler = signal(SIGALRM, null); + (void) alarm(timeout); + if (connect(s, (struct sockaddr *)&sin, sizeof (sin)) < 0) { + (void) alarm(0); + (void) signal(SIGALRM, old_handler); + err = errno; + (void) close(s); + errno = err; + if (errno == EADDRINUSE) { + goto retry; + } + /* + * If connecting to the local system fails, try + * again with "localhost" address instead. + */ + if (uts.nodename[0] == '\0') + (void) uname(&uts); + if (strcmp(host, uts.nodename) == 0) { + IN6_IPADDR_TO_V4MAPPED(htonl(INADDR_LOOPBACK), + &sin.sin6_addr); + sin.sin6_family = AF_INET6; + goto retry; + } + if (errno == ECONNREFUSED && timo <= 16) { + (void) sleep(timo); + timo *= 2; + goto retry; + } + return (NETWORK_ERROR_UNKNOWN); + } + (void) alarm(0); + (void) signal(SIGALRM, old_handler); + return (s); +} + + +/* + * net_close() closes a TCP connection opened by net_open() + */ +int +net_close(int nd) +{ + syslog(LOG_DEBUG, "net_close(%d)", nd); + return (close(nd)); +} + +/* + * net_read() reads up to the length specified into the buffer supplied from + * the network connection specified + */ +int +net_read(int nd, char *buf, int len) +{ + int rc; + void (*old_handler)(); + + syslog(LOG_DEBUG, "net_read(%d, 0x%x, %d)", nd, buf, len); + old_handler = signal(SIGALRM, null); + (void) alarm(read_wait_time_sec); + rc = read(nd, buf, len); + (void) alarm(0); + (void) signal(SIGALRM, old_handler); + return (rc); +} + + +/* + * net_write() writes the buffer specified out to the network connection + * supplied. + */ +int +net_write(int nd, char *buf, int len) +{ + int rc; + void (*old_handler)(); + + syslog(LOG_DEBUG, "net_write(%d, 0x%x, %d)", nd, buf, len); + old_handler = signal(SIGALRM, null); + (void) alarm(write_wait_time_sec); + rc = write(nd, buf, len); + (void) alarm(0); + (void) signal(SIGALRM, old_handler); + return (rc); +} + + +/* + * net_response() reads in a byte from the network connection and + * returns -1 if it isn't 0. + */ +int +net_response(int nd) +{ + char c; + + syslog(LOG_DEBUG, "net_response(%d)", nd); + if ((net_read(nd, &c, 1) != 1) || c) { + errno = EIO; + return (-1); + } + return (0); +} + +/* + * net_printf() sends a text message out to the network connection supplied + * using the same semantics as printf(3C) for stdio. + */ +/*PRINTFLIKE2*/ +int +net_printf(int nd, char *fmt, ...) +{ + char *buf; + va_list ap; + int err; + int size; + int rc; + + syslog(LOG_DEBUG, "net_printf(%d, %s, ...)", nd, fmt); + + if ((buf = malloc(BUFSIZ)) == NULL) { + err = errno; + syslog(LOG_DEBUG, "net_printf malloc failed"); + errno = err; + return (-1); + } + + va_start(ap, fmt); + size = vsnprintf(buf, BUFSIZ, fmt, ap); + if (size >= BUFSIZ) { + if ((buf = (char *)realloc(buf, size + 2)) == NULL) { + err = errno; + syslog(LOG_DEBUG, "net_printf malloc failed"); + errno = err; + return (-1); + } + size = vsnprintf(buf, size + 1, fmt, ap); + } + va_end(ap); + + + rc = net_write(nd, buf, (int)strlen(buf)); + free(buf); + return (rc); +} + +/* + * net_gets() read from the network connection until either a newline + * is encountered, or the buffer passed in is full. This is similiar + * to fgets(3C) + */ +char * +net_gets(char *buf, int bufSize, int nd) +{ + char tmp; + int count = 0; + + syslog(LOG_DEBUG, "net_gets(0x%x, %d, %d)", buf, bufSize, nd); + (void) memset(buf, NULL, bufSize); + while ((count < bufSize) && (net_read(nd, &tmp, 1) > 0)) + if ((buf[count++] = tmp) == '\n') break; + + if (count != 0) + return (buf); + return (NULL); +} + + +/* + * net_send_message() sends a message out the network connection using + * net_printf() and returns the result from net_response() + */ +/*PRINTFLIKE2*/ +int +net_send_message(int nd, char *fmt, ...) +{ + char buf[BUFSIZ]; + va_list ap; + + syslog(LOG_DEBUG, "net_send_message(%d, %s, ...)", nd, fmt); + va_start(ap, fmt); + if (vsnprintf(buf, sizeof (buf), fmt, ap) >= sizeof (buf)) { + syslog(LOG_ERR, "libprint:net_send_message: buffer overrun"); + return (-1); + } + va_end(ap); + + if (net_write(nd, buf, (int)((strlen(buf) != 0) ? strlen(buf) : 1)) < 0) + return (-1); + return (net_response(nd)); +} + + +/* + * net_send_file() sends the appropriate rfc1179 file transfer sub message + * to notify the remote side it is sending a file. It then sends the + * file if the remote side responds that it is ready. If the remote side + * can't accept the file an error is returned. If the transfer fails, + * an error is returned. + */ +int +net_send_file(int nd, char *name, char *data, int data_size, int type) +{ + char *truncated_name, + *mptr, + *fileBuf = NULL; + int count, + retries = 0, + size, + tmperrno; + + syslog(LOG_DEBUG, "net_send_file(%d, %s, 0x%x, %d, %d)", nd, + (name ? name : "NULL"), data, data_size, type); + if ((truncated_name = (char *)strrchr(name, '/')) == NULL) + truncated_name = name; + else + truncated_name++; + + + if (data == NULL) { + size = map_in_file(name, &fileBuf, 1); + mptr = fileBuf; + } else { + mptr = data; + size = data_size; + } + + if (size < 0) { + tmperrno = errno; /* because syslog() can change errno */ + syslog(LOG_DEBUG, "net_send_file(%d, %s, 0x%x, %d, %d): %m", nd, + (name ? name : "NULL"), data, data_size, type); + errno = tmperrno; + return (NETWORK_ERROR_UNKNOWN); + } + + /* send XFER command */ + if (net_send_message(nd, "%c%d %s\n", type, size, + truncated_name) != 0) { + (void) munmap(fileBuf, size); + errno = EIO; + return (NETWORK_ERROR_SEND_RESPONSE); + } + + /* send DATA and ACK */ + count = size; + while (count > 0) { + int rc; + + rc = net_write(nd, mptr, count); + + if (rc < 0) { + if (retries++ < 5) { + /* + * save/restore errno; + * will lose if syslogd down + */ + tmperrno = errno; + syslog(LOG_DEBUG, + "net_send_file error on write: %m %d", + retries); + errno = tmperrno; + continue; + } + /* save/restore errno; will lose if syslogd down */ + tmperrno = errno; + syslog(LOG_DEBUG, "net_send_file error on write: %m"); + if (fileBuf != NULL) + (void) munmap(fileBuf, size); + errno = tmperrno; + return (-1); + + } else { + count -= rc; + mptr += rc; + retries = 0; + } + } + + if (fileBuf != NULL) + (void) munmap(fileBuf, size); + + if (net_send_message(nd, "", NULL) != 0) { + errno = EIO; + return (NETWORK_ERROR_SEND_FAILED); + } + + return (0); +} |