Basic support for SMTP STARTTLS.
[cert-checker.git] / main.c
diff --git a/main.c b/main.c
index 8200be56bb29f23e25d876f347ae9a3bf5b9a802..3a8dab5b40cdf15f51b44cf19421dca996794996 100644 (file)
--- a/main.c
+++ b/main.c
 int warning_after = 30;
 int error_after = 7;
 int verbose = 0;
+int timeout = 9;
+int use_starttls = 0;
+
+#define BUFFER_SIZE 1024
 
 #define LOG_LEVEL 0
 
 #define die(msg) { fprintf(stderr, "Error: " msg "\n" ); exit(3); }
+#define dief(msg, ...) { fprintf(stderr, "Error: " msg "\n", __VA_ARGS__ ); exit(3); }
 #define gnutls_die(code) { gnutls_perror(code); exit(3); }
 
 char errmsg[256];
@@ -81,6 +86,25 @@ int tcp_open( char *hostname, char *service ) {
        return sfd;
 }
 
+/* Expects some data. This is just wrong and should be written correctly. */
+char *expect(int fd, char *str) {
+       char buffer[BUFFER_SIZE];
+       int len = strlen(str);
+       int bytes = read(fd, buffer, BUFFER_SIZE);
+       if (bytes < len) goto fail;
+       if (!strncmp(buffer,str,len)) {
+               while (bytes == BUFFER_SIZE)
+                       bytes = read(fd, buffer, BUFFER_SIZE) > 0;
+               return NULL;
+       }
+
+       fail:;
+       char *ptr = strdup(buffer);
+       while (bytes == BUFFER_SIZE)
+               bytes = read(fd, buffer, BUFFER_SIZE) > 0;
+       return ptr;
+}
+
 int check( char * hostname, char *service ) {
        int state = S_OK;
        int err;
@@ -105,13 +129,27 @@ int check( char * hostname, char *service ) {
 
        /* Connect to server */
 
-       int fd = tcp_open( hostname, service );
+       long fd = tcp_open( hostname, service );
        
        if (fd == -1) {
                state= S_UNREACHABLE;
                goto cleanup;
        }
 
+
+       /* StartTLS? */
+
+       if (use_starttls) {
+               expect(fd, "");
+               char buffer[] = "STARTTLS\n";
+               write(fd, buffer, sizeof(buffer));
+               char *b;
+               if ((b = expect(fd, "220 "))) {
+                       dief("STARTTLS declined: %s.", b);
+               }
+       }
+
+
        /* Socket opened, establish tls connection */
 
        /* Associate socket with session */
@@ -177,7 +215,7 @@ void log_func( int level, char *msg ) {
  * This signal handler is wrong, but it's just a failsafe. 
  */
 void sig_handler(int k) {
-       fputs("Timeout.", stderr);      
+       fputs("Timeout.\n", stderr);    
        exit(S_UNKNOWN);
 }
 
@@ -187,7 +225,7 @@ int main(int argc, char **argv) {
 
        int opt;
 
-       while ((opt = getopt(argc, argv, "hvw:c:H:p:s:")) != -1) {
+       while ((opt = getopt(argc, argv, "hvSw:c:H:p:s:t:")) != -1) {
                switch (opt) {
                        case 'w':
                                warning_after = atoi(optarg);
@@ -198,6 +236,10 @@ int main(int argc, char **argv) {
                        case 'H':
                                hostname = strdup(optarg);
                                break;
+                       case 't':
+                               timeout = atoi( optarg );
+                               if (timeout < 0) die("Timeout must be >= 0"); 
+                               break;
                        case 'p':                       
                        case 's':
                                if (service != NULL) die("Only one service can be specified.");
@@ -208,6 +250,8 @@ int main(int argc, char **argv) {
                                exit(0);
                        case 'v':
                                verbose++;
+                       case 'S':
+                               use_starttls = 1;
                        default: break;
                }
        }
@@ -233,12 +277,14 @@ int main(int argc, char **argv) {
        /* Setup alarm */
        /* TODO: doesn't work. */
        signal(SIGALRM, sig_handler);
-       alarm(9*60);
+       alarm( timeout );
 
        /* Do checking */
        state = check(hostname, service);
-       if (state < 0)
-               printf("Internal error.");
+       if (state < 0) {
+               sprintf(errmsg, "Internal error %i.", state);
+               state = S_UNKNOWN;
+       }
        
        gnutls_global_deinit();
 
@@ -253,9 +299,11 @@ void print_help() {
        printf(
                "Usage: cert-checker [options] -H hostname -p|s port|service\n"
                "  Where options could be: \n"
-               "       -h      this help\n"
-               "       -w      warning level (in days, default 30)\n"
-               "       -c      critical level (in days, default 7)\n"
-               "       -v      verbosity level\n"      
+               "       -h hostname   this help\n"
+               "       -w n          warning level (in days, default 30)\n"
+               "       -c n          critical level (in days, default 7)\n"
+               "       -v            verbosity level\n"        
+               "       -t n          timeout (in seconds, n=0 disables timeout\n"
+               "               -S            use SMTP/STARTLS\n"
        );
 }