+
+
+static int connectsock(char *host, char *service, char *protocol, int defaultPort)
+{
+ struct hostent *phe;
+ struct servent *pse;
+ struct protoent *ppe;
+ struct sockaddr_in sin;
+ int s, type;
+
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+
+ pse = getservbyname(service, protocol);
+ if (pse != NULL) {
+ sin.sin_port = pse->s_port;
+ }
+ else if (atoi(service) > 0) {
+ sin.sin_port = htons(atoi(service));
+ }
+ else {
+ sin.sin_port = htons(defaultPort);
+ }
+ phe = gethostbyname(host);
+ if (phe) {
+ memcpy(&sin.sin_addr, phe->h_addr, phe->h_length);
+ } else if ((sin.sin_addr.s_addr = inet_addr(host)) == INADDR_NONE) {
+ return -1;
+ }
+ if ((ppe = getprotobyname(protocol)) == 0) {
+ return -1;
+ }
+ if (!strcmp(protocol, "udp")) {
+ type = SOCK_DGRAM;
+ } else {
+ type = SOCK_STREAM;
+ }
+
+ s = socket(PF_INET, type, ppe->p_proto);
+ if (s < 0) {
+ return -1;
+ }
+
+ if (connect(s, (struct sockaddr *) &sin, sizeof(sin)) < 0) {
+ close(s);
+ return -1;
+ }
+
+ return (s);
+}
+
+static int uds_connectsock(int *isLocal, char *sockpath)
+{
+ struct sockaddr_un addr;
+ int s;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ safestrncpy(addr.sun_path, sockpath, sizeof addr.sun_path);
+
+ s = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (s < 0) {
+ return -1;
+ }
+
+ if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ close(s);
+ return -1;
+ }
+
+ *isLocal = 1;
+ return s;
+}
+
+
+/*
+ * input binary data from socket
+ */
+static void serv_read(CtdlIPC *ipc, char *buf, unsigned int bytes)
+{
+ unsigned int len, rlen;
+
+#if defined(HAVE_OPENSSL)
+ if (ipc->ssl) {
+ serv_read_ssl(ipc, buf, bytes);
+ return;
+ }
+#endif
+ len = 0;
+ while (len < bytes) {
+ rlen = read(ipc->sock, &buf[len], bytes - len);
+ if (rlen < 1) {
+ connection_died(ipc, 0);
+ return;
+ }
+ len += rlen;
+ }
+}
+
+
+/*
+ * send binary to server
+ */
+void serv_write(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
+{
+ unsigned int bytes_written = 0;
+ int retval;
+
+#if defined(HAVE_OPENSSL)
+ if (ipc->ssl) {
+ serv_write_ssl(ipc, buf, nbytes);
+ return;
+ }
+#endif
+ while (bytes_written < nbytes) {
+ retval = write(ipc->sock, &buf[bytes_written],
+ nbytes - bytes_written);
+ if (retval < 1) {
+ connection_died(ipc, 0);
+ return;
+ }
+ bytes_written += retval;
+ }
+}
+
+
+#ifdef HAVE_OPENSSL
+/*
+ * input binary data from encrypted connection
+ */
+static void serv_read_ssl(CtdlIPC* ipc, char *buf, unsigned int bytes)
+{
+ int len, rlen;
+ char junk[1];
+
+ len = 0;
+ while (len < bytes) {
+ if (SSL_want_read(ipc->ssl)) {
+ if ((SSL_write(ipc->ssl, junk, 0)) < 1) {
+ error_printf("SSL_write in serv_read:\n");
+ ERR_print_errors_fp(stderr);
+ }
+ }
+ rlen = SSL_read(ipc->ssl, &buf[len], bytes - len);
+ if (rlen < 1) {
+ long errval;
+
+ errval = SSL_get_error(ipc->ssl, rlen);
+ if (errval == SSL_ERROR_WANT_READ ||
+ errval == SSL_ERROR_WANT_WRITE) {
+ sleep(1);
+ continue;
+ }
+/***
+ Not sure why we'd want to handle these error codes any differently,
+ but this definitely isn't the way to handle them. Someone must have
+ naively assumed that we could fall back to unencrypted communications,
+ but all it does is just recursively blow the stack.
+ if (errval == SSL_ERROR_ZERO_RETURN ||
+ errval == SSL_ERROR_SSL) {
+ serv_read(ipc, &buf[len], bytes - len);
+ return;
+ }
+ ***/
+ error_printf("SSL_read in serv_read: %s\n",
+ ERR_reason_error_string(ERR_peek_error()));
+ connection_died(ipc, 1);
+ return;
+ }
+ len += rlen;
+ }
+}
+
+
+/*
+ * send binary to server encrypted
+ */
+static void serv_write_ssl(CtdlIPC *ipc, const char *buf, unsigned int nbytes)
+{
+ unsigned int bytes_written = 0;
+ int retval;
+ char junk[1];
+
+ while (bytes_written < nbytes) {
+ if (SSL_want_write(ipc->ssl)) {
+ if ((SSL_read(ipc->ssl, junk, 0)) < 1) {
+ error_printf("SSL_read in serv_write:\n");
+ ERR_print_errors_fp(stderr);
+ }
+ }
+ retval = SSL_write(ipc->ssl, &buf[bytes_written],
+ nbytes - bytes_written);
+ if (retval < 1) {
+ long errval;
+
+ errval = SSL_get_error(ipc->ssl, retval);
+ if (errval == SSL_ERROR_WANT_READ ||
+ errval == SSL_ERROR_WANT_WRITE) {
+ sleep(1);
+ continue;
+ }
+ if (errval == SSL_ERROR_ZERO_RETURN ||
+ errval == SSL_ERROR_SSL) {
+ serv_write(ipc, &buf[bytes_written],
+ nbytes - bytes_written);
+ return;
+ }
+ error_printf("SSL_write in serv_write: %s\n",
+ ERR_reason_error_string(ERR_peek_error()));
+ connection_died(ipc, 1);
+ return;
+ }
+ bytes_written += retval;
+ }
+}
+
+
+#ifdef THREADED_CLIENT
+static void ssl_lock(int mode, int n, const char *file, int line)
+{
+ if (mode & CRYPTO_LOCK)
+ pthread_mutex_lock(Critters[n]);
+ else
+ pthread_mutex_unlock(Critters[n]);
+}
+#endif /* THREADED_CLIENT */
+
+
+static void CtdlIPC_init_OpenSSL(void)
+{
+ int a;
+ SSL_METHOD *ssl_method;
+ DH *dh;
+
+ /* already done init */
+ if (ssl_ctx) {
+ return;
+ }
+
+ /* Get started */
+ a = 0;
+ ssl_ctx = NULL;
+ dh = NULL;
+ SSL_load_error_strings();
+ SSLeay_add_ssl_algorithms();
+
+ /* Set up the SSL context in which we will oeprate */
+ ssl_method = SSLv23_client_method();
+ ssl_ctx = SSL_CTX_new(ssl_method);
+ if (!ssl_ctx) {
+ error_printf("SSL_CTX_new failed: %s\n",
+ ERR_reason_error_string(ERR_get_error()));
+ return;
+ }
+ /* Any reasonable cipher we can get */
+ if (!(SSL_CTX_set_cipher_list(ssl_ctx, CIT_CIPHERS))) {
+ error_printf("No ciphers available for encryption\n");
+ return;
+ }
+ SSL_CTX_set_session_cache_mode(ssl_ctx, SSL_SESS_CACHE_BOTH);
+
+ /* Load DH parameters into the context */
+ dh = DH_new();
+ if (!dh) {
+ error_printf("Can't allocate a DH object: %s\n",
+ ERR_reason_error_string(ERR_get_error()));
+ return;
+ }
+ if (!(BN_hex2bn(&(dh->p), DH_P))) {
+ error_printf("Can't assign DH_P: %s\n",
+ ERR_reason_error_string(ERR_get_error()));
+ DH_free(dh);
+ return;
+ }
+ if (!(BN_hex2bn(&(dh->g), DH_G))) {
+ error_printf("Can't assign DH_G: %s\n",
+ ERR_reason_error_string(ERR_get_error()));
+ DH_free(dh);
+ return;
+ }
+ dh->length = DH_L;
+ SSL_CTX_set_tmp_dh(ssl_ctx, dh);
+ DH_free(dh);
+
+#ifdef THREADED_CLIENT
+ /* OpenSSL requires callbacks for threaded clients */
+ CRYPTO_set_locking_callback(ssl_lock);
+ CRYPTO_set_id_callback(id_callback);
+
+ /* OpenSSL requires us to do semaphores for threaded clients */
+ Critters = malloc(CRYPTO_num_locks() * sizeof (pthread_mutex_t *));
+ if (!Critters) {
+ perror("malloc failed");
+ exit(1);
+ } else {
+ for (a = 0; a < CRYPTO_num_locks(); a++) {
+ Critters[a] = malloc(sizeof (pthread_mutex_t));
+ if (!Critters[a]) {
+ perror("malloc failed");
+ exit(1);
+ }
+ pthread_mutex_init(Critters[a], NULL);
+ }
+ }
+#endif /* THREADED_CLIENT */
+}
+
+
+
+#ifdef THREADED_CLIENT
+static unsigned long id_callback(void) {
+ return (unsigned long)pthread_self();
+}
+#endif /* THREADED_CLIENT */
+#endif /* HAVE_OPENSSL */
+
+
+int
+ReadNetworkChunk(CtdlIPC* ipc)
+{
+ fd_set read_fd;
+ int tries;
+ int ret = 0;
+ int err = 0;
+ struct timeval tv;
+ size_t n;
+
+ tv.tv_sec = 1;
+ tv.tv_usec = 1000;
+ tries = 0;
+ n = 0;
+ while (1)
+ {
+ errno=0;
+ FD_ZERO(&read_fd);
+ FD_SET(ipc->sock, &read_fd);
+ ret = select(ipc->sock+1, &read_fd, NULL, NULL, &tv);
+
+// fprintf(stderr, "\nselect failed: %d %d %s\n", ret, err, strerror(err));
+
+ if (ret > 0) {
+
+ *(ipc->BufPtr) = '\0';
+// n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
+ n = recv(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1, 0);
+ if (n > 0) {
+ ipc->BufPtr[n]='\0';
+ ipc->BufUsed += n;
+ return n;
+ }
+ else
+ return n;
+ }
+ else if (ret < 0) {
+ if (!(errno == EINTR || errno == EAGAIN))
+ error_printf( "\nselect failed: %d %s\n", err, strerror(err));
+ return -1;
+ }/*
+ else {
+ tries ++;
+ if (tries >= 10)
+ n = read(ipc->sock, ipc->BufPtr, ipc->BufSize - (ipc->BufPtr - ipc->Buf) - 1);
+ if (n > 0) {
+ ipc->BufPtr[n]='\0';
+ ipc->BufUsed += n;
+ return n;
+ }
+ else {
+ connection_died(ipc, 0);
+ return -1;
+ }
+ }*/
+ }
+}
+
+/*
+ * input string from socket - implemented in terms of serv_read()
+ */
+#ifdef CHUNKED_READ
+
+static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
+{
+ int i, ntries;
+ char *aptr, *bptr, *aeptr, *beptr;
+
+// error_printf("---\n");
+
+ beptr = buf + SIZ;
+#if defined(HAVE_OPENSSL)
+ if (ipc->ssl) {
+
+ /* Read one character at a time. */
+ for (i = 0;; i++) {
+ serv_read(ipc, &buf[i], 1);
+ if (buf[i] == '\n' || i == (SIZ-1))
+ break;
+ }
+
+ /* If we got a long line, discard characters until the newline. */
+ if (i == (SIZ-1))
+ while (buf[i] != '\n')
+ serv_read(ipc, &buf[i], 1);
+
+ /* Strip the trailing newline (and carriage return, if present) */
+ if (i>=0 && buf[i] == 10) buf[i--] = 0;
+ if (i>=0 && buf[i] == 13) buf[i--] = 0;
+ }
+ else
+#endif
+ {
+ if (ipc->Buf == NULL)
+ {
+ ipc->BufSize = SIZ;
+ ipc->Buf = (char*) malloc(ipc->BufSize + 10);
+ *(ipc->Buf) = '\0';
+ ipc->BufPtr = ipc->Buf;
+ }
+
+ ntries = 0;
+// while ((ipc->BufUsed == 0)||(ntries++ > 10))
+ if (ipc->BufUsed == 0)
+ ReadNetworkChunk(ipc);
+
+//// if (ipc->BufUsed != 0) while (1)
+ bptr = buf;
+
+ while (1)
+ {
+ aptr = ipc->BufPtr;
+ aeptr = ipc->Buf + ipc->BufSize;
+ while ((aptr < aeptr) &&
+ (bptr < beptr) &&
+ (*aptr != '\0') &&
+ (*aptr != '\n'))
+ *(bptr++) = *(aptr++);
+ if ((*aptr == '\n') && (aptr < aeptr))
+ {
+ /* Terminate it right, remove the line breaks */
+ while ((aptr < aeptr) && ((*aptr == '\n') || (*aptr == '\r')))
+ aptr ++;
+ while ((aptr < aeptr ) && (*(aptr + 1) == '\0') )
+ aptr ++;
+ *(bptr++) = '\0';
+// fprintf(stderr, "parsing %d %d %d - %d %d %d %s\n", ipc->BufPtr - ipc->Buf, aptr - ipc->BufPtr, ipc->BufUsed , *aptr, *(aptr-1), *(aptr+1), buf);
+ if ((bptr > buf + 1) && (*(bptr-1) == '\r'))
+ *(--bptr) = '\0';
+
+ /* is there more in the buffer we need to read later? */
+ if (ipc->Buf + ipc->BufUsed > aptr)
+ {
+ ipc->BufPtr = aptr;
+ }
+ else
+ {
+ ipc->BufUsed = 0;
+ ipc->BufPtr = ipc->Buf;
+ }
+// error_printf("----bla6\n");
+ return;
+
+ }/* should we move our read stuf to the bufferstart so we have more space at the end? */
+ else if ((ipc->BufPtr != ipc->Buf) &&
+ (ipc->BufUsed > (ipc->BufSize - (ipc->BufSize / 4))))
+ {
+ size_t NewBufSize = ipc->BufSize * 2;
+ int delta = (ipc->BufPtr - ipc->Buf);
+ char *NewBuf;
+
+ /* if the line would end after our buffer, we should use a bigger buffer. */
+ NewBuf = (char *)malloc (NewBufSize + 10);
+ memcpy (NewBuf, ipc->BufPtr, ipc->BufUsed - delta);
+ free(ipc->Buf);
+ ipc->Buf = ipc->BufPtr = NewBuf;
+ ipc->BufUsed -= delta;
+ ipc->BufSize = NewBufSize;
+ }
+ if (ReadNetworkChunk(ipc) <0)
+ {
+// error_printf("----bla\n");
+ return;
+ }
+ }
+/// error_printf("----bl45761%s\nipc->BufUsed");
+ }
+// error_printf("----bla1\n");
+}
+
+#else /* CHUNKED_READ */
+
+static void CtdlIPC_getline(CtdlIPC* ipc, char *buf)
+{
+ int i;
+
+ /* Read one character at a time. */
+ for (i = 0;; i++) {
+ serv_read(ipc, &buf[i], 1);
+ if (buf[i] == '\n' || i == (SIZ-1))
+ break;
+ }
+
+ /* If we got a long line, discard characters until the newline. */
+ if (i == (SIZ-1))
+ while (buf[i] != '\n')
+ serv_read(ipc, &buf[i], 1);
+
+ /* Strip the trailing newline (and carriage return, if present) */
+ if (i>=0 && buf[i] == 10) buf[i--] = 0;
+ if (i>=0 && buf[i] == 13) buf[i--] = 0;
+}
+
+
+#endif /* CHUNKED_READ */
+
+
+void CtdlIPC_chat_recv(CtdlIPC* ipc, char* buf)
+{
+ CtdlIPC_getline(ipc, buf);
+}
+
+/*
+ * send line to server - implemented in terms of serv_write()
+ */
+static void CtdlIPC_putline(CtdlIPC *ipc, const char *buf)
+{
+ char *cmd = NULL;
+ int len;
+
+ len = strlen(buf);
+ cmd = malloc(len + 2);
+ if (!cmd) {
+ /* This requires no extra memory */
+ serv_write(ipc, buf, len);
+ serv_write(ipc, "\n", 1);
+ } else {
+ /* This is network-optimized */
+ strncpy(cmd, buf, len);
+ strcpy(cmd + len, "\n");
+ serv_write(ipc, cmd, len + 1);
+ free(cmd);
+ }
+
+ ipc->last_command_sent = time(NULL);
+}
+
+void CtdlIPC_chat_send(CtdlIPC* ipc, const char* buf)
+{
+ CtdlIPC_putline(ipc, buf);
+}
+
+
+/*
+ * attach to server
+ */
+CtdlIPC* CtdlIPC_new(int argc, char **argv, char *hostbuf, char *portbuf)
+{
+ int a;
+ char cithost[SIZ];
+ char citport[SIZ];
+ char sockpath[SIZ];
+ CtdlIPC* ipc;
+
+ ipc = ialloc(CtdlIPC);
+ if (!ipc) {
+ return 0;
+ }
+#if defined(HAVE_OPENSSL)
+ ipc->ssl = NULL;
+ CtdlIPC_init_OpenSSL();
+#endif
+#if defined(HAVE_PTHREAD_H)
+ pthread_mutex_init(&(ipc->mutex), NULL); /* Default fast mutex */
+#endif
+ ipc->sock = -1; /* Not connected */
+ ipc->isLocal = 0; /* Not local, of course! */
+ ipc->downloading = 0;
+ ipc->uploading = 0;
+ ipc->last_command_sent = 0L;
+ ipc->network_status_cb = NULL;
+ ipc->Buf = NULL;
+ ipc->BufUsed = 0;
+ ipc->BufPtr = NULL;
+
+ strcpy(cithost, DEFAULT_HOST); /* default host */
+ strcpy(citport, DEFAULT_PORT); /* default port */
+
+ /* Allow caller to supply our values (Windows) */
+ if (hostbuf && strlen(hostbuf) > 0)
+ strcpy(cithost, hostbuf);
+ if (portbuf && strlen(portbuf) > 0)
+ strcpy(citport, portbuf);
+
+ /* Read host/port from command line if present */
+ for (a = 0; a < argc; ++a) {
+ if (a == 0) {
+ /* do nothing */
+ } else if (a == 1) {
+ strcpy(cithost, argv[a]);
+ } else if (a == 2) {
+ strcpy(citport, argv[a]);
+ } else {
+ error_printf("%s: usage: ",argv[0]);
+ error_printf("%s [host] [port] ",argv[0]);
+ ifree(ipc);
+ errno = EINVAL;
+ return 0;
+ }
+ }
+
+ if ((!strcmp(cithost, "localhost"))
+ || (!strcmp(cithost, "127.0.0.1"))) {
+ ipc->isLocal = 1;
+ }
+
+ /* If we're using a unix domain socket we can do a bunch of stuff */
+ if (!strcmp(cithost, UDS)) {
+ if (!strcasecmp(citport, DEFAULT_PORT)) {
+ snprintf(sockpath, sizeof sockpath, file_citadel_socket);
+ }
+ else {
+ snprintf(sockpath, sizeof sockpath, "%s/%s", citport, "citadel.socket");
+ }
+ ipc->sock = uds_connectsock(&(ipc->isLocal), sockpath);
+ if (ipc->sock == -1) {
+ ifree(ipc);
+ return 0;
+ }
+ if (hostbuf != NULL) strcpy(hostbuf, cithost);
+ if (portbuf != NULL) strcpy(portbuf, sockpath);
+ return ipc;
+ }
+
+ ipc->sock = connectsock(cithost, citport, "tcp", 504);
+ if (ipc->sock == -1) {
+ ifree(ipc);
+ return 0;
+ }
+ if (hostbuf != NULL) strcpy(hostbuf, cithost);
+ if (portbuf != NULL) strcpy(portbuf, citport);
+ return ipc;
+}
+
+
+/*
+ * Disconnect and delete the IPC class (destructor)
+ */
+void CtdlIPC_delete(CtdlIPC* ipc)
+{
+#ifdef HAVE_OPENSSL
+ if (ipc->ssl) {
+ SSL_shutdown(ipc->ssl);
+ SSL_free(ipc->ssl);
+ ipc->ssl = NULL;
+ }
+#endif
+ if (ipc->sock > -1) {
+ shutdown(ipc->sock, 2); /* Close it up */
+ ipc->sock = -1;
+ }
+ if (ipc->Buf != NULL)
+ free (ipc->Buf);
+ ipc->Buf = NULL;
+ ipc->BufPtr = NULL;
+ ifree(ipc);
+}
+
+
+/*
+ * Disconnect and delete the IPC class (destructor)
+ * Also NULLs out the pointer
+ */
+void CtdlIPC_delete_ptr(CtdlIPC** pipc)
+{
+ CtdlIPC_delete(*pipc);
+ *pipc = NULL;
+}
+
+
+/*
+ * return the file descriptor of the server socket so we can select() on it.
+ *
+ * FIXME: This is only used in chat mode; eliminate it when chat mode gets
+ * rewritten...
+ */
+int CtdlIPC_getsockfd(CtdlIPC* ipc)
+{
+ return ipc->sock;
+}
+
+
+/*
+ * return one character
+ *
+ * FIXME: This is only used in chat mode; eliminate it when chat mode gets
+ * rewritten...
+ */
+char CtdlIPC_get(CtdlIPC* ipc)
+{
+ char buf[2];
+ char ch;
+
+ serv_read(ipc, buf, 1);
+ ch = (int) buf[0];
+
+ return (ch);
+}