"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.7/smtp.c" (16 Feb 2018, 10378 Bytes) of package /linux/misc/s-nail-14.9.7.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.6_vs_14.9.7.

    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       n_err(">>> %s", X);\
  138    if (!(n_poption & n_PO_DEBUG))\
  139       swrite(sp, X);\
  140 } while (0)
  141 
  142 static bool_t
  143 _smtp_talk(struct sock *sp, struct sendbundle *sbp) /* TODO n_string etc. */
  144 {
  145    char o[LINESIZE];
  146    char const *hostname;
  147    struct smtp_line _sl, *slp = &_sl;
  148    struct str b64;
  149    struct name *n;
  150    size_t blen, cnt;
  151    bool_t inhdr = TRU1, inbcc = FAL0, rv = FAL0;
  152    NYD_ENTER;
  153 
  154    hostname = n_nodename(TRU1);
  155    slp->buf = NULL;
  156    slp->bufsize = 0;
  157 
  158    /* Read greeting */
  159    _ANSWER(2, FAL0, FAL0);
  160 
  161 #ifdef HAVE_SSL
  162    if (!sp->s_use_ssl && xok_blook(smtp_use_starttls, &sbp->sb_url, OXM_ALL)) {
  163       snprintf(o, sizeof o, NETLINE("EHLO %s"), hostname);
  164       _OUT(o);
  165       _ANSWER(2, FAL0, FAL0);
  166 
  167       _OUT(NETLINE("STARTTLS"));
  168       _ANSWER(2, FAL0, FAL0);
  169 
  170       if (!(n_poption & n_PO_DEBUG) && ssl_open(&sbp->sb_url, sp) != OKAY)
  171          goto jleave;
  172    }
  173 #else
  174    if (xok_blook(smtp_use_starttls, &sbp->sb_url, OXM_ALL)) {
  175       n_err(_("No SSL support compiled in\n"));
  176       goto jleave;
  177    }
  178 #endif
  179 
  180    /* Shorthand: no authentication, plain HELO? */
  181    if (sbp->sb_ccred.cc_authtype == AUTHTYPE_NONE) {
  182       snprintf(o, sizeof o, NETLINE("HELO %s"), hostname);
  183       _OUT(o);
  184       _ANSWER(2, FAL0, FAL0);
  185       goto jsend;
  186    }
  187 
  188    /* We'll have to deal with authentication */
  189    snprintf(o, sizeof o, NETLINE("EHLO %s"), hostname);
  190    _OUT(o);
  191    _ANSWER(2, FAL0, FAL0);
  192 
  193    switch (sbp->sb_ccred.cc_authtype) {
  194    default:
  195       /* FALLTHRU (doesn't happen) */
  196    case AUTHTYPE_PLAIN:
  197       cnt = sbp->sb_ccred.cc_user.l;
  198       if(sbp->sb_ccred.cc_pass.l >= UIZ_MAX - 2 ||
  199             cnt >= UIZ_MAX - 2 - sbp->sb_ccred.cc_pass.l){
  200 jerr_cred:
  201          n_err(_("Credentials overflow buffer sizes\n"));
  202          goto jleave;
  203       }
  204       cnt += sbp->sb_ccred.cc_pass.l;
  205 
  206       if(cnt >= sizeof(o) - 2)
  207          goto jerr_cred;
  208       cnt += 2;
  209       if(b64_encode_calc_size(cnt) == UIZ_MAX)
  210          goto jerr_cred;
  211 
  212       _OUT(NETLINE("AUTH PLAIN"));
  213       _ANSWER(3, FAL0, FAL0);
  214 
  215       snprintf(o, sizeof o, "%c%s%c%s",
  216          '\0', sbp->sb_ccred.cc_user.s, '\0', sbp->sb_ccred.cc_pass.s);
  217       if(b64_encode_buf(&b64, o, cnt, B64_SALLOC | B64_CRLF) == NULL)
  218          goto jleave;
  219       _OUT(b64.s);
  220       _ANSWER(2, FAL0, FAL0);
  221       break;
  222    case AUTHTYPE_LOGIN:
  223       if(b64_encode_calc_size(sbp->sb_ccred.cc_user.l) == UIZ_MAX ||
  224             b64_encode_calc_size(sbp->sb_ccred.cc_pass.l) == UIZ_MAX)
  225          goto jerr_cred;
  226 
  227       _OUT(NETLINE("AUTH LOGIN"));
  228       _ANSWER(3, FAL0, FAL0);
  229 
  230       if(b64_encode_buf(&b64, sbp->sb_ccred.cc_user.s, sbp->sb_ccred.cc_user.l,
  231             B64_SALLOC | B64_CRLF) == NULL)
  232          goto jleave;
  233       _OUT(b64.s);
  234       _ANSWER(3, FAL0, FAL0);
  235 
  236       if(b64_encode_buf(&b64, sbp->sb_ccred.cc_pass.s, sbp->sb_ccred.cc_pass.l,
  237             B64_SALLOC | B64_CRLF) == NULL)
  238          goto jleave;
  239       _OUT(b64.s);
  240       _ANSWER(2, FAL0, FAL0);
  241       break;
  242 #ifdef HAVE_MD5
  243    case AUTHTYPE_CRAM_MD5:{
  244       char *cp;
  245 
  246       _OUT(NETLINE("AUTH CRAM-MD5"));
  247       _ANSWER(3, FAL0, TRU1);
  248 
  249       if((cp = cram_md5_string(&sbp->sb_ccred.cc_user, &sbp->sb_ccred.cc_pass,
  250             slp->dat)) == NULL)
  251          goto jerr_cred;
  252       _OUT(cp);
  253       _ANSWER(2, FAL0, FAL0);
  254       }break;
  255 #endif
  256 #ifdef HAVE_GSSAPI
  257    case AUTHTYPE_GSSAPI:
  258       if (n_poption & n_PO_DEBUG)
  259          n_err(_(">>> We would perform GSS-API authentication now\n"));
  260       else if (!_smtp_gssapi(sp, sbp, slp))
  261          goto jleave;
  262       break;
  263 #endif
  264    }
  265 
  266 jsend:
  267    snprintf(o, sizeof o, NETLINE("MAIL FROM:<%s>"), sbp->sb_url.url_u_h.s);
  268    _OUT(o);
  269    _ANSWER(2, FAL0, FAL0);
  270 
  271    for(n = sbp->sb_to; n != NULL; n = n->n_flink){
  272       if (!(n->n_type & GDEL)) { /* TODO should not happen!?! */
  273          if(n->n_flags & NAME_ADDRSPEC_WITHOUT_DOMAIN)
  274             snprintf(o, sizeof o, NETLINE("RCPT TO:<%s@%s>"),
  275                skinned_name(n), hostname);
  276          else
  277             snprintf(o, sizeof o, NETLINE("RCPT TO:<%s>"), skinned_name(n));
  278          _OUT(o);
  279          _ANSWER(2, FAL0, FAL0);
  280       }
  281    }
  282 
  283    _OUT(NETLINE("DATA"));
  284    _ANSWER(3, FAL0, FAL0);
  285 
  286    fflush_rewind(sbp->sb_input);
  287    cnt = fsize(sbp->sb_input);
  288    while (fgetline(&slp->buf, &slp->bufsize, &cnt, &blen, sbp->sb_input, 1)
  289          != NULL) {
  290       if (inhdr) {
  291          if (*slp->buf == '\n')
  292             inhdr = inbcc = FAL0;
  293          else if (inbcc && blankchar(*slp->buf))
  294             continue;
  295          /* We know what we have generated first, so do not look for whitespace
  296           * before the ':' */
  297          else if (!ascncasecmp(slp->buf, "bcc: ", 5)) {
  298             inbcc = TRU1;
  299             continue;
  300          } else
  301             inbcc = FAL0;
  302       }
  303 
  304       if (n_poption & n_PO_DEBUG) {
  305          slp->buf[blen - 1] = '\0';
  306          n_err(">>> %s%s\n", (*slp->buf == '.' ? "." : n_empty), slp->buf);
  307          continue;
  308       }
  309       if (*slp->buf == '.')
  310          swrite1(sp, ".", 1, 1); /* TODO I/O rewrite.. */
  311       slp->buf[blen - 1] = NETNL[0];
  312       slp->buf[blen] = NETNL[1];
  313       swrite1(sp, slp->buf, blen + 1, 1);
  314    }
  315    _OUT(NETLINE("."));
  316    _ANSWER(2, FAL0, FAL0);
  317 
  318    _OUT(NETLINE("QUIT"));
  319    _ANSWER(2, TRU1, FAL0);
  320    rv = TRU1;
  321 jleave:
  322    if (slp->buf != NULL)
  323       free(slp->buf);
  324    NYD_LEAVE;
  325    return rv;
  326 }
  327 
  328 #ifdef HAVE_GSSAPI
  329 # include "smtp-gssapi.h"
  330 #endif
  331 
  332 #undef _OUT
  333 #undef _ANSWER
  334 
  335 FL bool_t
  336 smtp_mta(struct sendbundle *sbp)
  337 {
  338    struct sock so;
  339    sighandler_type volatile saveterm;
  340    bool_t volatile rv = FAL0;
  341    NYD_ENTER;
  342 
  343    saveterm = safe_signal(SIGTERM, SIG_IGN);
  344    if (sigsetjmp(_smtp_jmp, 1))
  345       goto jleave;
  346    if (saveterm != SIG_IGN)
  347       safe_signal(SIGTERM, &_smtp_onterm);
  348 
  349    memset(&so, 0, sizeof so);
  350    if (!(n_poption & n_PO_DEBUG) && !sopen(&so, &sbp->sb_url))
  351       goto jleave;
  352 
  353    so.s_desc = "SMTP";
  354    rv = _smtp_talk(&so, sbp);
  355 
  356    if (!(n_poption & n_PO_DEBUG))
  357       sclose(&so);
  358 jleave:
  359    safe_signal(SIGTERM, saveterm);
  360    NYD_LEAVE;
  361    return rv;
  362 }
  363 #endif /* HAVE_SMTP */
  364 
  365 /* s-it-mode */