"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.11/smtp.c" (8 Aug 2018, 10691 Bytes) of package /linux/misc/s-nail-14.9.11.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.10_vs_14.9.11.

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