summaryrefslogtreecommitdiff
path: root/usr/src/lib/print/libprint/common/network.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/print/libprint/common/network.c')
-rw-r--r--usr/src/lib/print/libprint/common/network.c418
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);
+}