no way to compare when less than two revisions

Differences

This shows you the differences between two versions of the page.


Last revision
sdk:scripts:email-to-sms [2015/05/05 15:04] – external edit 127.0.0.1
Line 1: Line 1:
 +====== SDK Script email-to-sms.are ======
 +<code c email-to-sms.are>
 +/* DESC: This script implements a lightweight SMTP server which is able to receive mail and forward them as SMS to a phone number.
 + * Copyright (C) 2012 NetModule AG, Switzerland
 + */
 +
 +MAX_CONECTIONS  = 1;                                    /* maximum of concurrent connections */
 +TIMEOUT         = 10;                                   /* receive timeout in seconds */
 +MAX_ERRORS      = 5;                                    /* max. number of error per session */
 +MAX_MESSAGES    = 32;                                   /* max. number of messages per session */
 +MAX_HEADER_LEN  = 1024;                                 /* max. header length */
 +MAX_BODY_LEN    = 160;                                  /* max. body length */
 +DOMAIN          = "domain.net";                         /* recipient domain */
 +AUTHENTICATE    = 1;                                    /* force authentication */
 +
 +ALLOWED_SENDERS = mkarray("sender1@domain.local",        
 +   "sender2@domain.local");
 +  
 +ALLOWED_NUMBERS = mkarray("+123456789",                
 +           "+987654321");
 +
 +string extract_addr (string str)
 +{
 +    addr = trim(str);
 +
 +    /* check for @ */
 +    pos = strchr(addr, "@");
 +    if (is_void(pos) || pos == 0) {
 +        nb_syslog("malformed address");
 +        return "";
 +    }
 +
 +    pos = strchr(addr, "<");
 +    if (!is_void(pos)) {
 +        addr = substr(addr, pos + 1);
 +        pos = strchr(addr, ">");
 +        if (is_void(pos) || pos < 1) {
 +            nb_syslog("malformed address");
 +            return "";
 +        }
 +        addr = left(addr, pos);
 +    }
 +
 +    return addr;
 +}
 +
 +string validate_sender (string from) 
 +{
 +    /* extract mail addr */
 +    sender = extract_addr(from);
 +    if (strlen(sender) == 0) return "";
 +
 +    /* check if sender is allowed */
 +    for (i = 0; i < length(ALLOWED_SENDERS); i++) {
 +        s = ALLOWED_SENDERS[i];
 +        if (left(sender, strlen(s)) == s) {
 +            nb_syslog("sender %s can pass", sender);
 +            return sender;
 +        }
 +    }
 +    nb_syslog("sender %s is now allowed to send something", sender);
 +    return "";
 +}
 +
 +string validate_number (string rcpt)
 +{
 +    /* extract mail addr */
 +    addr = extract_addr(rcpt);
 +    if (strlen(addr) == 0) return "";
 +
 +    pos = strchr(addr, "@");
 +    if (is_void(pos) || pos == 0) {
 +        nb_syslog("malformed address");
 +        return "";
 +    }
 +
 +    number = left(addr, pos);
 +    domain = substr(addr, pos + 1);
 +
 +    if (domain != DOMAIN) {
 +        nb_syslog("invalid domain");
 +        return "";
 +    }
 +    if (left(number, 1) != "+") {
 +        nb_syslog("need international number format");
 +        return "";
 +    }
 +
 +    for (i = 0; i < length(ALLOWED_NUMBERS); i++) {
 +        n = ALLOWED_NUMBERS[i];
 +        if (left(number, strlen(n)) == n) {
 +            nb_syslog("number %s is allowed", number);
 +            return number;
 +        }
 +    }
 +    nb_syslog("number %s is not allowed", number);
 +    return number;
 +}
 +
 +string add_to_header (string header, string msg)
 +{
 +    if ((strlen(header) + strlen(msg) + 1) > MAX_HEADER_LEN) {
 +        nb_syslog("header length exceeded");
 +        return header;
 +    }
 +    return strcat(header, msg, "\n");
 +}
 +
 +string add_to_body (string body, string msg)
 +{
 +    len = strlen(body) + strlen(msg) + 1;
 +
 +    if (len > MAX_BODY_LEN) {
 +        nb_syslog("body length exceeded (%d bytes, max %d)", len, MAX_BODY_LEN);
 +        return body;
 +    }
 +    return strcat(body, msg, "\n");
 +}
 +
 +void usage()
 +{
 +    nb_syslog("usage: email-to-sms.are <port>");
 +    exit(1);
 +}
 +
 +/* main */
 +
 +if (argc < 2) {
 +    usage();
 +}
 +
 +PORT = (int) argv[1];
 +HOSTNAME = nb_config_get("network.hostname");
 +if (strlen(hostname) == 0) {
 +    HOSTNAME = "host";
 +}
 +if (AUTHENTICATE) {
 +    PASSWORD = nb_config_get("admin.password");
 +    if (strlen(PASSWORD) == 0) {
 +        PASSWORD = "admin01";
 +    }
 +}
 +
 +st = nb_config_get("smsd.status");
 +if (st != "1") {
 +    nb_syslog("SMS daemon is not enabled");
 +    exit(1);
 +}
 +
 +if (PORT < 1024 || PORT >= 65536) {                
 +    nb_syslog("invalid port %d specified (must be 1024..65535)", PORT);
 +    exit(1);                                                           
 +}     
 +
 +/* raise socket */
 +
 +sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
 +if (sock < 0) {
 +    nb_syslog("unable to open socket");
 +    exit(1);
 +}
 +
 +ret = bind(sock, PORT, "");
 +if (ret == -1) {
 +    nb_syslog("unable to bind port %d", PORT);
 +    close(sock);
 +    exit(1);
 +}
 +
 +ret = listen(sock, MAX_CONECTIONS);
 +if (ret == -1) {
 +    nb_syslog("unable to listen");
 +    close(sock);
 +    exit(1);
 +}
 +
 +/* listen for clients */
 +nb_syslog("listening for connections on port %d", PORT);
 +state = "disconnected";
 +
 +while (1) {
 +    client = accept(sock);
 +    if (client < 0) {
 +        printf("accept failed\n");
 +        sleep(1);
 +        continue;
 +    }   
 +    
 +    nb_syslog("new client has connected");
 +    user = pass = number = rcpt = sender = header = body = subject = "";
 +    errors = messages = 0;
 +
 +    state = "connected";
 +    send(client, "220 %s.%s ESTMP arena\r\n", HOSTNAME, DOMAIN);    
 +
 +    while (1) {
 +        /* wait for socket data */
 +        rv = select(client, TIMEOUT);
 +        if (rv == -1) {
 +            nb_syslog("select failed, client endpoint stalled");
 +            break;
 +        } else if (rv == 0) {
 +            nb_syslog("select timed out"); 
 +            break;
 +        } else {
 +            /* data available */
 +            msg = recv(client);
 +            if (strlen(msg) == 0) {
 +                nb_syslog("empty message received, stopping transaction");
 +                break;
 +            }
 +            messages++;
 +
 +            if (errors > MAX_ERRORS || messages > MAX_MESSAGES) {     
 +                nb_syslog("discarding client because of too many errors");
 +                send(client,"554 Transaction failed\r\n");
 +                break;
 +            }
 +
 +            pos1 = strchr(msg, "\r");
 +            pos2 = strchr(msg, "\n");
 +            if (pos1 >= 0 && pos1 < pos2) {
 +                pos = pos1;
 +            } else {
 +                pos = pos2;
 +            }
 +            if (!is_void(pos)) {
 +                line = substr(msg, 0, pos); 
 +                cmd = tolower(line);
 +            } else {
 +                /* not a valid command */
 +                line = cmd = "";
 +            }
 +
 +            /*
 +                dump(msg);
 +                nb_syslog("state is '%s', got cmd '%s'", state, cmd);
 +             */
 +
 +            if (left(cmd, 4) == "quit" || left(cmd, 3) == "bye") {
 +                send(client,"221 Bye\r\n");
 +                state = "disconnected";                
 +                break;
 +            } 
 +
 +            if (state == "connected") {
 +                /* expect hello */
 +                if (left(cmd, 4) == "helo") {
 +                    state = "heloSent";
 +                    send(client, "250 %s.%s Hello\r\n", HOSTNAME, DOMAIN);
 +                } else if (left(cmd, 4) == "ehlo") {
 +                    if (AUTHENTICATE) {
 +                        state = "heloSent";
 +                        send(client, 
 +                             "250-%s.%s at your service\r\n250-8BITMIME\r\n250-AUTH LOGIN PLAIN\r\n250 ENHANCEDSTATUSCODES\r\n",
 +                             HOSTNAME, DOMAIN);
 +                    } else {
 +                        state = "heloSent";
 +                        send(client, "220 %s.%s Hello\r\n", HOSTNAME, DOMAIN);
 +                    }
 +                } else {
 +                    send(client, "500 polite people say hello first\r\n");
 +                    errors++;
 +                }
 +            } else if (state == "heloSent" || state == "authenticated") {
 +                if (AUTHENTICATE && state != "authenticated") {
 +                    if (left(cmd, 10) == "auth login") {
 +                        /* prompt for username */
 +                        send(client, "334 %s\r\n", base64_encode("Username:"));
 +                        state = "userPrompted";
 +                    } else {
 +                        send(client, "500 not authenticated yet\r\n");
 +                        errors++;
 +                    }
 +                } else {
 +                    /* expect mail from */
 +                    if (left(cmd, 10) == "mail from:") {
 +                        /* rewind mail settings */
 +                        user = pass = number = rcpt = sender = header = body = subject = "";
 +
 +                        sender = validate_sender(substr(cmd, 10, 64));
 +                        if (strlen(sender) > 0) {
 +                            state = "fromReceived";
 +                            send(client, "250 sender ok\r\n");
 +                        } else {
 +                            send(client, "500 invalid sender\r\n");
 +                            errors++;
 +                        }
 +                    } else {
 +                        send(client, "500 need mail from\r\n");
 +                        errors++;
 +                    }
 +                }
 +            } else if (state == "userPrompted") {
 +                /* read username */
 +                user = base64_decode(trim(line));
 +                if (user != "admin") {
 +                    nb_syslog("invalid user specified for authentication");
 +                    send(client, "500 invalid user specified\r\n");
 +                    errors++;
 +                } else {
 +                    send(client, "334 %s\r\n", base64_encode("Password:"));
 +                    state = "passwordPrompted";
 +                }
 +            } else if (state == "passwordPrompted") {
 +                /* read username */
 +                pass = base64_decode(trim(line));
 +                if (pass != PASSWORD) {
 +                    nb_syslog("authentication for '%s' failed", user);
 +                    send(client, "500 authentication failed\r\n");
 +                    errors++;
 +                } else {
 +                    nb_syslog("authentication succeeded for user %s", user);
 +                    send(client, "235 OK\r\n");
 +                    state = "authenticated";
 +                }
 +            } else if (state == "fromReceived") {
 +                /* expect rcpt to */
 +                if (left(cmd, 8) == "rcpt to:") {
 +                    number = validate_number(substr(cmd, 8, 64));
 +                    if (strlen(number) > 0) {
 +                        state = "rcptReceived";
 +                        send(client, "250 rcpt ok\r\n");
 +                    } else {
 +                        send(client, "500 invalid rcpt\r\n");
 +                        errors++;
 +                    }
 +                } else {
 +                    send(client, "500 need rcpt to\r\n");
 +                    errors++;
 +                }
 +            } else if (state == "rcptReceived") {
 +                /* expect DATA */
 +                if (left(cmd, 4) == "data") {
 +                    state = "dataReady";
 +                    send(client, "354 End data with <CR><LF>.<CR><LF>\r\n");
 +                } else {
 +                    send(client, "500 need data\r\n");
 +                    errors++;
 +                }
 +            } else if (state == "dataReady" || state == "headerReceived") {
 +                gotEOF = 0;
 +                pos = strstr(msg, "\r\n.\r\n");
 +                if (!is_void(pos)) {
 +                    gotEOF = 1;
 +                    msg = left(msg, pos);
 +                }
 +
 +                if (state != "headerReceived") {
 +                    pos = strstr(msg, "\n\n");
 +                    if (is_void(pos)) pos = strstr(msg, "\r\n\r\n");
 +
 +                    if (is_void(pos)) {
 +                        header = add_to_header(header, msg);
 +                    } else {
 +                        state = "headerReceived";
 +                        header = add_to_header(header, substr(msg, 0, pos));
 +                        body = add_to_body(body, ltrim(substr(msg, pos+1)));
 +                    }
 +                } else {
 +                    body = add_to_body(body, msg, "\n");
 +                }
 +
 +                if (gotEOF) {
 +                    if (state != "headerReceived") {
 +                        nb_syslog("no body message data has been received");
 +                        send(client,"554 Transaction failed (no body data)\r\n");
 +                        errors++;
 +                    } else {
 +                        body = trim(body);
 +                        if (strlen(body) > 0 && strlen(number) > 0) {
 +                            /* dump(body); */
 +
 +                            nb_syslog("sending message to %s (%d bytes)", number, strlen(body));
 +                            id = nb_sms_send(number, body);                
 +                            if (id) {
 +                                nb_syslog("spooled message to %s (ID %s)", number, id);
 +                                send(client,"250 OK message queued as %s\r\n", id);
 +                                errors = 0;
 +                            } else {
 +                                nb_syslog("unable to send message to %s", number);
 +                                send(client,"554 Transaction failed\r\n");
 +                                errors++;
 +                            }
 +                        } else {
 +                            nb_syslog("no message data");
 +                            send(client,"554 Transaction failed (no message data)\r\n");
 +                            errors++;
 +                        }
 +                    }
 +                    state = "heloSent";                    
 +                }
 +            } else {
 +                nb_syslog("rejecting client due to invalid state");
 +                break;
 +            }
 +        }
 +    }
 +    nb_syslog("disconnecting client");
 +    close(client);
 +}
 +
 +nb_syslog("terminating");
 +close(sock);
 +
 +exit(0);
 +
 +</code>