mirror of
https://github.com/warmcat/libwebsockets.git
synced 2025-03-16 00:00:07 +01:00

The %.*s is very handy to print strings where you have a length, but there is no NUL termination. It's quite widely supported but at least one vendor RTOS toolchain doesn't have it. Since there aren't that many uses of it yet, audit all uses and convert to a new helper lws_strnncpy() which uses the smaller of two lengths.
382 lines
9.1 KiB
C
382 lines
9.1 KiB
C
/*
|
|
* libwebsockets - small server side websockets and web server implementation
|
|
*
|
|
* Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to
|
|
* deal in the Software without restriction, including without limitation the
|
|
* rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
|
* sell copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
* IN THE SOFTWARE.
|
|
*/
|
|
|
|
#include "private-lib-core.h"
|
|
#include "private-lib-abstract.h"
|
|
|
|
/** enum lwsgs_smtp_states - where we are in SMTP protocol sequence */
|
|
typedef enum lwsgs_smtp_states {
|
|
LGSSMTP_IDLE, /**< awaiting new email */
|
|
LGSSMTP_CONNECTING, /**< opening tcp connection to MTA */
|
|
LGSSMTP_CONNECTED, /**< tcp connection to MTA is connected */
|
|
/* (server sends greeting) */
|
|
LGSSMTP_SENT_HELO, /**< sent the HELO */
|
|
|
|
LGSSMTP_SENT_FROM, /**< sent FROM */
|
|
LGSSMTP_SENT_TO, /**< sent TO */
|
|
LGSSMTP_SENT_DATA, /**< sent DATA request */
|
|
LGSSMTP_SENT_BODY, /**< sent the email body */
|
|
|
|
/*
|
|
* (server sends, eg, "250 Ok: queued as 12345")
|
|
* at this point we can return to LGSSMTP_SENT_HELO and send a
|
|
* new email, or continue below to QUIT, or just wait
|
|
*/
|
|
|
|
LGSSMTP_SENT_QUIT, /**< sent the session quit */
|
|
|
|
/* (server sends, eg, "221 Bye" and closes the connection) */
|
|
} lwsgs_smtp_states_t;
|
|
|
|
/** abstract protocol instance data */
|
|
|
|
typedef struct lws_smtp_client_protocol {
|
|
const struct lws_abs *abs;
|
|
lwsgs_smtp_states_t estate;
|
|
|
|
lws_smtp_email_t *e; /* the email we are trying to send */
|
|
const char *helo;
|
|
|
|
unsigned char send_pending:1;
|
|
} lws_smtpcp_t;
|
|
|
|
static const short retcodes[] = {
|
|
0, /* idle */
|
|
0, /* connecting */
|
|
220, /* connected */
|
|
250, /* helo */
|
|
250, /* from */
|
|
250, /* to */
|
|
354, /* data */
|
|
250, /* body */
|
|
221, /* quit */
|
|
};
|
|
|
|
static void
|
|
lws_smtpc_state_transition(lws_smtpcp_t *c, lwsgs_smtp_states_t s)
|
|
{
|
|
lwsl_debug("%s: cli %p: state %d -> %d\n", __func__, c, c->estate, s);
|
|
c->estate = s;
|
|
}
|
|
|
|
static lws_smtp_email_t *
|
|
lws_smtpc_get_email(lws_smtpcp_t *c)
|
|
{
|
|
const lws_token_map_t *tm;
|
|
|
|
/* ... the email we want to send */
|
|
tm = lws_abs_get_token(c->abs->ap_tokens, LTMI_PSMTP_V_LWS_SMTP_EMAIL_T);
|
|
if (!tm) {
|
|
assert(0);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
return (lws_smtp_email_t *)tm->u.value;
|
|
}
|
|
|
|
/*
|
|
* Called when something happened so that we know now the final disposition of
|
|
* the email send attempt, for good or ill.
|
|
*
|
|
* Inform the owner via the done callback and set up the next queued one if any.
|
|
*
|
|
* Returns nonzero if we queued a new one
|
|
*/
|
|
|
|
static int
|
|
lws_smtpc_email_disposition(lws_smtpcp_t *c, int disp, const void *buf,
|
|
size_t len)
|
|
{
|
|
lws_smtpcp_t *ch;
|
|
lws_abs_t *ach;
|
|
lws_dll2_t *d;
|
|
|
|
lws_smtpc_state_transition(c, LGSSMTP_SENT_HELO);
|
|
|
|
/* lifetime of the email object is handled by done callback */
|
|
c->e->done(c->e, c->e->data, disp, buf, len);
|
|
c->e = NULL;
|
|
|
|
/* this may not be the time to try to send anything else... */
|
|
|
|
if (disp == LWS_SMTP_DISPOSITION_FAILED_DESTROY)
|
|
return 0;
|
|
|
|
/* ... otherwise... do we have another queued? */
|
|
|
|
d = lws_dll2_get_tail(&c->abs->children_owner);
|
|
if (!d)
|
|
return 0;
|
|
|
|
ach = lws_container_of(d, lws_abs_t, bound);
|
|
ch = (lws_smtpcp_t *)ach->api;
|
|
|
|
c->e = lws_smtpc_get_email(ch);
|
|
|
|
/* since we took it on, remove it from the queue */
|
|
lws_dll2_remove(d);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* we became connected
|
|
*/
|
|
|
|
static int
|
|
lws_smtpc_abs_accept(lws_abs_protocol_inst_t *api)
|
|
{
|
|
lws_smtpcp_t *c = (lws_smtpcp_t *)api;
|
|
|
|
/* we have become connected in the tcp sense */
|
|
|
|
lws_smtpc_state_transition(c, LGSSMTP_CONNECTED);
|
|
|
|
/*
|
|
* From the accept(), the next thing that should happen is the SMTP
|
|
* server sends its greeting like "220 smtp2.example.com ESMTP Postfix",
|
|
* we'll hear about it in the rx callback, or time out
|
|
*/
|
|
|
|
c->abs->at->set_timeout(c->abs->ati,
|
|
PENDING_TIMEOUT_AWAITING_SERVER_RESPONSE, 3);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
lws_smtpc_abs_rx(lws_abs_protocol_inst_t *api, const uint8_t *buf, size_t len)
|
|
{
|
|
lws_smtpcp_t *c = (lws_smtpcp_t *)api;
|
|
char dotstar[96], at[5];
|
|
int n;
|
|
|
|
c->abs->at->set_timeout(c->abs->ati, NO_PENDING_TIMEOUT, 0);
|
|
|
|
lws_strncpy(at, (const char *)buf, sizeof(at));
|
|
n = atoi(at);
|
|
|
|
switch (c->estate) {
|
|
case LGSSMTP_CONNECTED:
|
|
if (n != 220) {
|
|
/*
|
|
* The server did not properly greet us... we can't
|
|
* even get started, so fail the transport connection
|
|
* (and anything queued on it)
|
|
*/
|
|
|
|
lws_strnncpy(dotstar, buf, len, sizeof(dotstar));
|
|
lwsl_err("%s: server: %s\n", __func__, dotstar);
|
|
|
|
return 1;
|
|
}
|
|
break;
|
|
|
|
case LGSSMTP_SENT_BODY:
|
|
/*
|
|
* We finished one way or another... let's prepare to send a
|
|
* new one... or wait until server hangs up on us
|
|
*/
|
|
if (!lws_smtpc_email_disposition(c,
|
|
n == 250 ? LWS_SMTP_DISPOSITION_SENT :
|
|
LWS_SMTP_DISPOSITION_FAILED,
|
|
"destroyed", 0))
|
|
return 0; /* become idle */
|
|
|
|
break; /* ask to send */
|
|
|
|
case LGSSMTP_SENT_QUIT:
|
|
lwsl_debug("%s: done\n", __func__);
|
|
lws_smtpc_state_transition(c, LGSSMTP_IDLE);
|
|
|
|
return 1;
|
|
|
|
default:
|
|
if (n != retcodes[c->estate]) {
|
|
lws_strnncpy(dotstar, buf, len, sizeof(dotstar));
|
|
lwsl_notice("%s: bad response: %d (state %d) %s\n",
|
|
__func__, n, c->estate, dotstar);
|
|
|
|
lws_smtpc_email_disposition(c,
|
|
LWS_SMTP_DISPOSITION_FAILED, buf, len);
|
|
|
|
return 0;
|
|
}
|
|
break;
|
|
}
|
|
|
|
c->send_pending = 1;
|
|
c->abs->at->ask_for_writeable(c->abs->ati);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
lws_smtpc_abs_writeable(lws_abs_protocol_inst_t *api, size_t budget)
|
|
{
|
|
char b[256 + LWS_PRE], *p = b + LWS_PRE;
|
|
lws_smtpcp_t *c = (lws_smtpcp_t *)api;
|
|
int n;
|
|
|
|
if (!c->send_pending || !c->e)
|
|
return 0;
|
|
|
|
c->send_pending = 0;
|
|
|
|
lwsl_debug("%s: writing response for state %d\n", __func__, c->estate);
|
|
|
|
switch (c->estate) {
|
|
case LGSSMTP_CONNECTED:
|
|
n = lws_snprintf(p, sizeof(b) - LWS_PRE, "HELO %s\n", c->helo);
|
|
lws_smtpc_state_transition(c, LGSSMTP_SENT_HELO);
|
|
break;
|
|
|
|
case LGSSMTP_SENT_HELO:
|
|
n = lws_snprintf(p, sizeof(b) - LWS_PRE, "MAIL FROM: <%s>\n",
|
|
c->e->from);
|
|
lws_smtpc_state_transition(c, LGSSMTP_SENT_FROM);
|
|
break;
|
|
|
|
case LGSSMTP_SENT_FROM:
|
|
n = lws_snprintf(p, sizeof(b) - LWS_PRE,
|
|
"RCPT TO: <%s>\n", c->e->to);
|
|
lws_smtpc_state_transition(c, LGSSMTP_SENT_TO);
|
|
break;
|
|
|
|
case LGSSMTP_SENT_TO:
|
|
n = lws_snprintf(p, sizeof(b) - LWS_PRE, "DATA\n");
|
|
lws_smtpc_state_transition(c, LGSSMTP_SENT_DATA);
|
|
break;
|
|
|
|
case LGSSMTP_SENT_DATA:
|
|
p = (char *)&c->e[1];
|
|
n = strlen(p);
|
|
lws_smtpc_state_transition(c, LGSSMTP_SENT_BODY);
|
|
break;
|
|
|
|
case LGSSMTP_SENT_BODY:
|
|
n = lws_snprintf(p, sizeof(b) - LWS_PRE, "quit\n");
|
|
lws_smtpc_state_transition(c, LGSSMTP_SENT_QUIT);
|
|
break;
|
|
|
|
case LGSSMTP_SENT_QUIT:
|
|
return 0;
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
//puts(p);
|
|
c->abs->at->tx(c->abs->ati, (uint8_t *)p, n);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
lws_smtpc_abs_closed(lws_abs_protocol_inst_t *api)
|
|
{
|
|
lws_smtpcp_t *c = (lws_smtpcp_t *)api;
|
|
|
|
if (c)
|
|
lws_smtpc_state_transition(c, LGSSMTP_IDLE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Creating for initial transport and for piggybacking on another transport
|
|
* both get created here the same. But piggybackers have ai->bound attached.
|
|
*/
|
|
|
|
static int
|
|
lws_smtpc_create(const lws_abs_t *ai)
|
|
{
|
|
lws_smtpcp_t *c = (lws_smtpcp_t *)ai->api;
|
|
|
|
memset(c, 0, sizeof(*c));
|
|
|
|
c->abs = ai;
|
|
c->e = lws_smtpc_get_email(c);
|
|
|
|
lws_smtpc_state_transition(c, lws_dll2_is_detached(&ai->bound) ?
|
|
LGSSMTP_CONNECTING : LGSSMTP_IDLE);
|
|
|
|
/* If we are initiating the transport, we will get an accept() next...
|
|
*
|
|
* If we are piggybacking, the parent will get a .child_bind() after
|
|
* this to give it a chance to act on us joining (eg, it was completely
|
|
* idle and we joined).
|
|
*/
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
lws_smtpc_destroy(lws_abs_protocol_inst_t **_c)
|
|
{
|
|
lws_smtpcp_t *c = (lws_smtpcp_t *)*_c;
|
|
|
|
if (!c)
|
|
return;
|
|
|
|
/* so if we are still holding on to c->e, we have failed to send it */
|
|
if (c->e)
|
|
lws_smtpc_email_disposition(c,
|
|
LWS_SMTP_DISPOSITION_FAILED_DESTROY, "destroyed", 0);
|
|
|
|
*_c = NULL;
|
|
}
|
|
|
|
static int
|
|
lws_smtpc_compare(lws_abs_t *abs1, lws_abs_t *abs2)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
lws_smtpc_child_bind(lws_abs_t *abs)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* events the transport invokes (handled by abstract protocol) */
|
|
|
|
const lws_abs_protocol_t lws_abs_protocol_smtp = {
|
|
.name = "smtp",
|
|
.alloc = sizeof(lws_smtpcp_t),
|
|
.flags = LWSABSPR_FLAG_PIPELINE,
|
|
|
|
.create = lws_smtpc_create,
|
|
.destroy = lws_smtpc_destroy,
|
|
.compare = lws_smtpc_compare,
|
|
|
|
.accept = lws_smtpc_abs_accept,
|
|
.rx = lws_smtpc_abs_rx,
|
|
.writeable = lws_smtpc_abs_writeable,
|
|
.closed = lws_smtpc_abs_closed,
|
|
.heartbeat = NULL,
|
|
|
|
.child_bind = lws_smtpc_child_bind,
|
|
};
|