"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.10/smtp.c" (25 Mar 2018, 10646 Bytes) of package /linux/misc/s-nail-14.9.10.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "smtp.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 14.9.9_vs_14.9.10.

    1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
    2  *@ SMTP client.
    3  *@ TODO - use initial responses to save a round-trip (RFC 4954)
    4  *@ TODO - more (verbose) understanding+rection upon STATUS CODES
    5  *
    6  * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
    7  * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
    8  */
    9 /*
   10  * Copyright (c) 2000
   11  * Gunnar Ritter.  All rights reserved.
   12  *
   13  * Redistribution and use in source and binary forms, with or without
   14  * modification, are permitted provided that the following conditions
   15  * are met:
   16  * 1. Redistributions of source code must retain the above copyright
   17  *    notice, this list of conditions and the following disclaimer.
   18  * 2. Redistributions in binary form must reproduce the above copyright
   19  *    notice, this list of conditions and the following disclaimer in the
   20  *    documentation and/or other materials provided with the distribution.
   21  * 3. All advertising materials mentioning features or use of this software
   22  *    must display the following acknowledgement:
   23  *    This product includes software developed by Gunnar Ritter
   24  *    and his contributors.
   25  * 4. Neither the name of Gunnar Ritter nor the names of his contributors
   26  *    may be used to endorse or promote products derived from this software
   27  *    without specific prior written permission.
   28  *
   29  * THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
   30  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   31  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   32  * ARE DISCLAIMED.  IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
   33  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   34  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   35  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   36  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   37  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   38  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   39  * SUCH DAMAGE.
   40  */
   41 #undef n_FILE
   42 #define n_FILE smtp
   43 
   44 #ifndef HAVE_AMALGAMATION
   45 # include "nail.h"
   46 #endif
   47 
   48 EMPTY_FILE()
   49 #ifdef HAVE_SMTP
   50 #include <sys/socket.h>
   51 
   52 struct smtp_line {
   53    char     *dat;    /* Actual data */
   54    size_t   datlen;
   55    char     *buf;    /* Memory buffer */
   56    size_t   bufsize;
   57 };
   58 
   59 static sigjmp_buf _smtp_jmp;
   60 
   61 static void    _smtp_onterm(int signo);
   62 
   63 /* Get the SMTP server's answer, expecting val */
   64 static int     _smtp_read(struct sock *sp, struct smtp_line *slp, int val,
   65                   bool_t ign_eof, bool_t want_dat);
   66 
   67 /* Talk to a SMTP server */
   68 static bool_t  _smtp_talk(struct sock *sp, struct sendbundle *sbp);
   69 
   70 #ifdef HAVE_GSSAPI
   71 static bool_t  _smtp_gssapi(struct sock *sp, struct sendbundle *sbp,
   72                   struct smtp_line *slp);
   73 #endif
   74 
   75 static void
   76 _smtp_onterm(int signo)
   77 {
   78    NYD_X; /* Signal handler */
   79    n_UNUSED(signo);
   80    siglongjmp(_smtp_jmp, 1);
   81 }
   82 
   83 static int
   84 _smtp_read(struct sock *sp, struct smtp_line *slp, int val,
   85    bool_t ign_eof, bool_t want_dat)
   86 {
   87    int rv, len;
   88    char *cp;
   89    NYD_ENTER;
   90 
   91    do {
   92       if ((len = sgetline(&slp->buf, &slp->bufsize, NULL, sp)) < 6) {
   93          if (len >= 0 && !ign_eof)
   94             n_err(_("Unexpected EOF on SMTP connection\n"));
   95          rv = -1;
   96          goto jleave;
   97       }
   98       if (n_poption & n_PO_VERBVERB)
   99          n_err(slp->buf);
  100       switch (slp->buf[0]) {
  101       case '1':   rv = 1; break;
  102       case '2':   rv = 2; break;
  103       case '3':   rv = 3; break;
  104       case '4':   rv = 4; break;
  105       default:    rv = 5; break;
  106       }
  107       if (val != rv)
  108          n_err(_("smtp-server: %s"), slp->buf);
  109    } while (slp->buf[3] == '-');
  110 
  111    if (want_dat) {
  112       for (cp = slp->buf; digitchar(*cp); --len, ++cp)
  113          ;
  114       for (; blankchar(*cp); --len, ++cp)
  115          ;
  116       slp->dat = cp;
  117       assert(len >= 2);
  118       len -= 2;
  119       cp[slp->datlen = (size_t)len] = '\0';
  120    }
  121 jleave:
  122    NYD_LEAVE;
  123    return rv;
  124 }
  125 
  126 /* Indirect SMTP I/O */
  127 #define _ANSWER(X, IGNEOF, WANTDAT) \
  128 do if (!(n_poption & n_PO_DEBUG)) {\
  129    int y;\
  130    if ((y = _smtp_read(sp, slp, X, IGNEOF, WANTDAT)) != (X) &&\
  131          (!(IGNEOF) || y != -1))\
  132       goto jleave;\
  133 } while (0)
  134 #define _OUT(X) \
  135 do {\
  136    if (n_poption & n_PO_D_VV){\
  137       /* TODO for now n_err() cannot normalize newlines in %s expansions */\
  138       char *__x__ = savestr(X), *__y__ = &__x__[strlen(__x__)];\
  139       while(__y__ > __x__ && (__y__[-1] == '\n' || __y__[-1] == '\r'))\
  140          --__y__;\
  141       *__y__ = '\0';\
  142       n_err(">>> %s\n", __x__);\
  143    }\
  144    if (!(n_poption & n_PO_DEBUG))\
  145       swrite(sp, X);\
  146 } while (0)
  147 
  148 static bool_t
  149 _smtp_talk(struct sock *sp, struct sendbundle *sbp) /* TODO n_string etc. */
  150 {
  151    char o[LINESIZE];
  152    char const *hostname;
  153    struct smtp_line _sl, *slp = &_sl;
  154    struct str b64;
  155    struct name *n;
  156    size_t blen, cnt;
  157    bool_t inhdr = TRU1, inbcc = FAL0, rv = FAL0;
  158    NYD_ENTER;
  159 
  160    hostname = n_nodename(TRU1);
  161    slp->buf = NULL;
  162    slp->bufsize = 0;
  163 
  164    /* Read greeting */
  165    _ANSWER(2, FAL0, FAL0);
  166 
  167 #ifdef HAVE_SSL
  168    if (!sp->s_use_ssl && xok_blook(smtp_use_starttls, &sbp->sb_url, OXM_ALL)) {
  169       snprintf(o, sizeof o, NETLINE("EHLO %s"), hostname);
  170       _OUT(o);
  171       _ANSWER(2, FAL0, FAL0);
  172 
  173       _OUT(NETLINE("STARTTLS"));
  174       _ANSWER(2, FAL0, FAL0);
  175 
  176       if (!(n_poption & n_PO_DEBUG) && ssl_open(&sbp->sb_url, sp) != OKAY)
  177          goto jleave;
  178    }
  179 #else
  180    if (xok_blook(smtp_use_starttls, &sbp->sb_url, OXM_ALL)) {
  181       n_err(_("No SSL support compiled in\n"));
  182       goto jleave;
  183    }
  184 #endif
  185 
  186    /* Shorthand: no authentication, plain HELO? */
  187    if (sbp->sb_ccred.cc_authtype == AUTHTYPE_NONE) {
  188       snprintf(o, sizeof o, NETLINE("HELO %s"), hostname);
  189       _OUT(o);
  190       _ANSWER(2, FAL0, FAL0);
  191       goto jsend;
  192    }
  193 
  194    /* We'll have to deal with authentication */
  195    snprintf(o, sizeof o, NETLINE("EHLO %s"), hostname);
  196    _OUT(o);
  197    _ANSWER(2, FAL0, FAL0);
  198 
  199    switch (sbp->sb_ccred.cc_authtype) {
  200    default:
  201       /* FALLTHRU (doesn't happen) */
  202    case AUTHTYPE_PLAIN:
  203       cnt = sbp->sb_ccred.cc_user.l;
  204       if(sbp->sb_ccred.cc_pass.l >= UIZ_MAX - 2 ||
  205             cnt >= UIZ_MAX - 2 - sbp->sb_ccred.cc_pass.l){
  206 jerr_cred:
  207          n_err(_("Credentials overflow buffer sizes\n"));
  208          goto jleave;
  209       }
  210       cnt += sbp->sb_ccred.cc_pass.l;
  211 
  212       if(cnt >= sizeof(o) - 2)
  213          goto jerr_cred;
  214       cnt += 2;
  215       if(b64_encode_calc_size(cnt) == UIZ_MAX)
  216          goto jerr_cred;
  217 
  218       _OUT(NETLINE("AUTH PLAIN"));
  219       _ANSWER(3, FAL0, FAL0);
  220 
  221       snprintf(o, sizeof o, "%c%s%c%s",
  222          '\0', sbp->sb_ccred.cc_user.s, '\0', sbp->sb_ccred.cc_pass.s);
  223       if(b64_encode_buf(&b64, o, cnt, B64_SALLOC | B64_CRLF) == NULL)
  224          goto jleave;
  225       _OUT(b64.s);
  226       _ANSWER(2, FAL0, FAL0);
  227       break;
  228    case AUTHTYPE_LOGIN:
  229       if(b64_encode_calc_size(sbp->sb_ccred.cc_user.l) == UIZ_MAX ||
  230             b64_encode_calc_size(sbp->sb_ccred.cc_pass.l) == UIZ_MAX)
  231          goto jerr_cred;
  232 
  233       _OUT(NETLINE("AUTH LOGIN"));
  234       _ANSWER(3, FAL0, FAL0);
  235 
  236       if(b64_encode_buf(&b64, sbp->sb_ccred.cc_user.s, sbp->sb_ccred.cc_user.l,
  237             B64_SALLOC | B64_CRLF) == NULL)
  238          goto jleave;
  239       _OUT(b64.s);
  240       _ANSWER(3, FAL0, FAL0);
  241 
  242       if(b64_encode_buf(&b64, sbp->sb_ccred.cc_pass.s, sbp->sb_ccred.cc_pass.l,
  243             B64_SALLOC | B64_CRLF) == NULL)
  244          goto jleave;
  245       _OUT(b64.s);
  246       _ANSWER(2, FAL0, FAL0);
  247       break;
  248 #ifdef HAVE_MD5
  249    case AUTHTYPE_CRAM_MD5:{
  250       char *cp;
  251 
  252       _OUT(NETLINE("AUTH CRAM-MD5"));
  253       _ANSWER(3, FAL0, TRU1);
  254 
  255       if((cp = cram_md5_string(&sbp->sb_ccred.cc_user, &sbp->sb_ccred.cc_pass,
  256             slp->dat)) == NULL)
  257          goto jerr_cred;
  258       _OUT(cp);
  259       _ANSWER(2, FAL0, FAL0);
  260       }break;
  261 #endif
  262 #ifdef HAVE_GSSAPI
  263    case AUTHTYPE_GSSAPI:
  264       if (n_poption & n_PO_DEBUG)
  265          n_err(_(">>> We would perform GSS-API authentication now\n"));
  266       else if (!_smtp_gssapi(sp, sbp, slp))
  267          goto jleave;
  268       break;
  269 #endif
  270    }
  271 
  272 jsend:
  273    snprintf(o, sizeof o, NETLINE("MAIL FROM:<%s>"), sbp->sb_url.url_u_h.s);
  274    _OUT(o);
  275    _ANSWER(2, FAL0, FAL0);
  276 
  277    for(n = sbp->sb_to; n != NULL; n = n->n_flink){
  278       if (!(n->n_type & GDEL)) { /* TODO should not happen!?! */
  279          if(n->n_flags & NAME_ADDRSPEC_WITHOUT_DOMAIN)
  280             snprintf(o, sizeof o, NETLINE("RCPT TO:<%s@%s>"),
  281                skinned_name(n), hostname);
  282          else
  283             snprintf(o, sizeof o, NETLINE("RCPT TO:<%s>"), skinned_name(n));
  284          _OUT(o);
  285          _ANSWER(2, FAL0, FAL0);
  286       }
  287    }
  288 
  289    _OUT(NETLINE("DATA"));
  290    _ANSWER(3, FAL0, FAL0);
  291 
  292    fflush_rewind(sbp->sb_input);
  293    cnt = fsize(sbp->sb_input);
  294    while (fgetline(&slp->buf, &slp->bufsize, &cnt, &blen, sbp->sb_input, 1)
  295          != NULL) {
  296       if (inhdr) {
  297          if (*slp->buf == '\n')
  298             inhdr = inbcc = FAL0;
  299          else if (inbcc && blankchar(*slp->buf))
  300             continue;
  301          /* We know what we have generated first, so do not look for whitespace
  302           * before the ':' */
  303          else if (!ascncasecmp(slp->buf, "bcc: ", 5)) {
  304             inbcc = TRU1;
  305             continue;
  306          } else
  307             inbcc = FAL0;
  308       }
  309 
  310       if (n_poption & n_PO_DEBUG) {
  311          slp->buf[blen - 1] = '\0';
  312          n_err(">>> %s%s\n", (*slp->buf == '.' ? "." : n_empty), slp->buf);
  313          continue;
  314       }
  315       if (*slp->buf == '.')
  316          swrite1(sp, ".", 1, 1); /* TODO I/O rewrite.. */
  317       slp->buf[blen - 1] = NETNL[0];
  318       slp->buf[blen] = NETNL[1];
  319       swrite1(sp, slp->buf, blen + 1, 1);
  320    }
  321    _OUT(NETLINE("."));
  322    _ANSWER(2, FAL0, FAL0);
  323 
  324    _OUT(NETLINE("QUIT"));
  325    _ANSWER(2, TRU1, FAL0);
  326    rv = TRU1;
  327 jleave:
  328    if (slp->buf != NULL)
  329       free(slp->buf);
  330    NYD_LEAVE;
  331    return rv;
  332 }
  333 
  334 #ifdef HAVE_GSSAPI
  335 # include "smtp-gssapi.h"
  336 #endif
  337 
  338 #undef _OUT
  339 #undef _ANSWER
  340 
  341 FL bool_t
  342 smtp_mta(struct sendbundle *sbp)
  343 {
  344    struct sock so;
  345    sighandler_type volatile saveterm;
  346    bool_t volatile rv = FAL0;
  347    NYD_ENTER;
  348 
  349    saveterm = safe_signal(SIGTERM, SIG_IGN);
  350    if (sigsetjmp(_smtp_jmp, 1))
  351       goto jleave;
  352    if (saveterm != SIG_IGN)
  353       safe_signal(SIGTERM, &_smtp_onterm);
  354 
  355    memset(&so, 0, sizeof so);
  356    if (!(n_poption & n_PO_DEBUG) && !sopen(&so, &sbp->sb_url))
  357       goto jleave;
  358 
  359    so.s_desc = "SMTP";
  360    rv = _smtp_talk(&so, sbp);
  361 
  362    if (!(n_poption & n_PO_DEBUG))
  363       sclose(&so);
  364 jleave:
  365    safe_signal(SIGTERM, saveterm);
  366    NYD_LEAVE;
  367    return rv;
  368 }
  369 #endif /* HAVE_SMTP */
  370 
  371 /* s-it-mode */