Logo Search packages:      
Sourcecode: teapop version File versions

pop_socket.c

/* $Id: //depot/Teapop/0.3/teapop/pop_socket.c#8 $ */

/*
 * Copyright (c) 1999-2003 ToonTown Consulting
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the company nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/types.h>
#include <sys/time.h>
#include <sys/socket.h>

#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <errno.h>
#include <fcntl.h>
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>

#if __STDC__
#include <stdarg.h>
#else
#include <varargs.h>
#endif

#include "config.h"
#include "teapop.h"
#include "pop_signal.h"
#include "pop_socket.h"
#include "pop_strings.h"

/** BASED ON sshd.c FROM openssh.com */
#ifdef HAVE_TCPD_H
#include <tcpd.h>
#endif
#ifdef WITH_TCPD
int allow_severity = LOG_INFO;
int deny_severity = LOG_WARNING;
static char *progname = "teapop";
#endif

void
#if __STDC__
pop_socket_send(FILE * fd, const char *fmt, ...)
#else
pop_socket_send(fd, fmt, va_alist)
FILE *fd;
char *fmt;
va_dcl
#endif
{
      char buf[512];

      va_list ap;
#if __STDC__
      va_start(ap, fmt);
#else
      va_start(ap);
#endif
      vsnprintf(buf, 508, fmt, ap);
      if (!(buf[strlen(buf - 2)] == '\r' && buf[strlen(buf - 1)] == '\n'))
            strcat(buf, "\r\n");
      va_end(ap);

      (void) fputs(buf, fd);
      (void) fflush(fd);
}

int
pop_socket_rawsend(fd, buffer)
FILE *fd;
char *buffer;
{
      register int retval;

      retval = fputs(buffer, fd);
      return (retval == -1 ? 1 : 0);
}

/* UGH! */
int
#if __STDC__
pop_wait_for_commands(int timeout, int size, char *arguments, ...)
#else
pop_wait_for_commands(timeout, size, arguments, va_alist)
char *arguments;
int timeout, size;
va_dcl
#endif
{
      char buf[300], slask[512];
      static char *ptr;
      int arg;
      static va_list ap;
#if __STDC__
      va_start(ap, arguments);
#else
      va_start(ap, arguments);
#endif
      if ((ptr = va_arg(ap, char *)) == NULL)
      {
            va_end(ap);
            return (-1);
      }

      for (;;) {
            alarm(timeout);
            if (setjmp(env)) {
                  va_end(ap);
                  return (0);
            }
            if ((fgets(buf, 255, stdin)) == NULL) {
                  if (feof(stdin))
                        pop_signal_sigpipe(SIGPIPE);
                  else
                        return (-1);
            }

            if (buf[strlen(buf) - 1] != '\n') {
                  slask[0] = '\0';
                  while (slask[strlen(slask) - 1] != '\n')
                        fgets(slask, 500, stdin);
            }
            alarm(0);

            while (buf[strlen(buf) - 1] == '\n' ||
                buf[strlen(buf) - 1] == '\r') buf[strlen(buf) - 1] = '\0';
            arg = 1;
            while (ptr != NULL) {
                  if (!strncasecmp(ptr, buf, strlen(ptr)) &&
                      (strlen(buf) == strlen(ptr) ||
                        buf[strlen(ptr)] == ' ')) {
                        strncpy(arguments,
                            buf + strlen(ptr) + 1, size - 1);
                        arguments[size - 1] = '\0';
                        return (arg);
                  }
                  ptr = va_arg(ap, char *);
                  arg++;
            }
            ptr = strchr(buf, ' ');
            if (ptr != NULL)
                  *ptr = '\0';
            fprintf(stderr, "%s " POP_UNKNOWN "\r\n", POP_ERR, buf);
            va_end(ap);
            va_start(ap, arguments);
            ptr = va_arg(ap, char *);
      }

      va_end(ap);
      return (-1);
}

int
pop_socket_init(pinfo)
POP_INFO *pinfo;
{
      socklen_t len;

#ifdef INET6
      struct sockaddr_storage ss;
      char hbuf[NI_MAXHOST];
#else
      struct sockaddr_in name;
      struct hostent *hp;
#endif

#ifdef INET6
      len = sizeof(ss);
#else
      len = sizeof(name);
#endif


      /* Get our own IP */
#ifdef INET6
      if (getsockname(pinfo->insck, (struct sockaddr *)&ss, &len) == -1) {
            syslog(LOG_ERR, "can't do getsockname() (errno = %d)", errno);
            return (1);
      }
#else
      if (getsockname(pinfo->insck, (struct sockaddr *) &name, &len) == -1) {
            syslog(LOG_ERR, "can't do getsockname() (errno = %d)", errno);
            return (1);
      }
#endif

#ifdef INET6
      /* XXX - Is there an AF-independent way of doing this?? */
      switch (ss.ss_family) {
      case AF_INET:
            inet_ntop(ss.ss_family,
                &((struct sockaddr_in *)&ss)->sin_addr, hbuf, NI_MAXHOST);
            break;
      case AF_INET6:
            inet_ntop(ss.ss_family,
                &((struct sockaddr_in6 *)&ss)->sin6_addr, hbuf, NI_MAXHOST);
            break;
      default:
            syslog(LOG_ERR, "Unknown AF type on socket");
      }
      strncpy(pinfo->localip, hbuf, sizeof(pinfo->localip));
      pinfo->localip[sizeof(pinfo->localip)] = '\0';
#else
      strncpy(pinfo->localip, (char *)inet_ntoa(name.sin_addr),
          sizeof(pinfo->localip));
#endif
      pinfo->localip[sizeof(pinfo->localip)] = '\0';

      /* Get IP of the remote client */
#ifdef INET6
      if (getpeername(pinfo->insck, (struct sockaddr *)&ss, &len) == -1) {
            syslog(LOG_ERR, "can't get socket and/or do getpeername "
                "(error = %d)", errno);
            return (1);
      }
      if (getnameinfo((struct sockaddr *)&ss, len, hbuf,
                 sizeof(hbuf), NULL, 0, NI_NUMERICHOST)) {
                  syslog(LOG_NOTICE,
                      "can't get IP of remote client (error = %d)",
                      errno);
      } else {
            strncpy(pinfo->remoteip, hbuf, sizeof(pinfo->remoteip));
            pinfo->remoteip[sizeof(pinfo->remoteip)] = '\0';
      }
#else
      if (getpeername(pinfo->insck, (struct sockaddr *) &name, &len) == -1) {
            syslog(LOG_ERR, "can't get socket and/or do getpeername "
                "(error = %d)", errno);
            return (1);
      }
      strncpy(pinfo->remoteip, (char *) inet_ntoa(name.sin_addr),
          sizeof(pinfo->remoteip));
      pinfo->remoteip[sizeof(pinfo->remoteip)] = '\0';
#endif

      /* Now resolv the IP and see what we get */
      if (pinfo->nodns < 2) {
#ifdef INET6
            if (getnameinfo((struct sockaddr *)&ss, len, hbuf,
                 sizeof(hbuf), NULL, 0, NI_NAMEREQD)) {
                  syslog(LOG_NOTICE,
                      "can't do reverse dns on client (error = %d)",
                      errno);
            } else {
                  strncpy(pinfo->remotehost, hbuf,
                      sizeof(pinfo->remotehost));
                  pinfo->remotehost[sizeof(pinfo->remotehost)] = '\0';
            }
#else
            hp = gethostbyaddr((char *) &name.sin_addr,
                sizeof(name.sin_addr), AF_INET);
            if (hp == NULL) {
                  syslog(LOG_NOTICE,
                      "can't do reverse dns on client (error = %d)",
                      errno);
            } else {
                  strncpy(pinfo->remotehost, hp->h_name,
                      sizeof(pinfo->remotehost));
                  pinfo->remotehost[sizeof(pinfo->remotehost)] = '\0';
            }
#endif
      }
      if (pinfo->remotehost[0] == '\0') {
            strncpy(pinfo->remotehost, "unknown",
                sizeof(pinfo->remotehost));
            pinfo->remotehost[sizeof(pinfo->remotehost)] = '\0';
      }

#ifdef WITH_TCPD
      {
            struct request_info req;

            request_init(&req, RQ_DAEMON, progname, RQ_FILE, pinfo->insck, NULL);
            fromhost(&req);

            if (!hosts_access(&req)) {
                  syslog(LOG_ERR,
                      "tcp_wrappers connection refused %s %s ",
                      pinfo->remoteip,pinfo->remotehost);
                  return (2);
            }
      }
#endif
      pinfo->out = fdopen(pinfo->outsck, "wb");
      return (0);
}

int
pop_socket_bind(void)
{
      int sckfd, sck_opt = 1;

      struct sockaddr_in name;

      if ((sckfd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
            syslog(LOG_ERR, "Problem calling socket(): %s",
                strerror(errno));
            perror("socket");
            return (0);
      }
      /* westr - allow startup with reusing the sockets */
      if ((setsockopt(sckfd, SOL_SOCKET, SO_REUSEADDR, (char *)&sck_opt,
          sizeof(sck_opt))) < 0) {
            perror("setsockopt");
            return (0);
      }
      memset((char *) &name, 0, sizeof(struct sockaddr_in));
      name.sin_family = AF_INET;
      name.sin_port = htons(POP3PORT);
      if ((bind(sckfd, (struct sockaddr *) &name, sizeof(name))) < 0) {
            syslog(LOG_ERR, "Problem calling bind(): %s",
                strerror(errno));
            perror("bind");
            return (0);
      }
      if ((listen(sckfd, 10)) < 0) {
            syslog(LOG_ERR, "Problem calling listen(): %s",
                strerror(errno));
            perror("listen");
            return (0);
      }

      /*
       * XXX - for now we do not set the socket nonblock since it
       * causes more, or rather worse, problems then it fixes. - ibo
       */
/*
      if (fcntl(sckfd, F_SETFL, O_NONBLOCK) != 0) {
            syslog(LOG_ERR, "Problem calling fcntl(): %s",
                strerror(errno));
            perror("fcntl");
            return (0);
      }
*/
      return (sckfd);
}

int
pop_socket_wait(sckfd)
int sckfd;
{
       fd_set rfds;
       /* struct timeval tv; */

       FD_ZERO(&rfds);
       FD_SET(sckfd, &rfds);
       /*
         tv.tv_sec = 5;
         tv.tv_usec = 0;
       */

       /*
         Wait for sckfd to be ready and return.
         Will return number of FDs ready (1) on success.
         Will return 0 on timeout, -1 on error.
       */
       return (select(sckfd+1,&rfds,0,0,NULL));
}

Generated by  Doxygen 1.6.0   Back to index