stream: New functions stream_verify_name() and pstream_verify_name().
[openvswitch] / lib / stream.c
index 1a0ea7ba7466699ca80f1318e16a963e8ad1277c..ed497d693300c9bfba3511700ee78552ee2231a5 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2008, 2009 Nicira Networks.
+ * Copyright (c) 2008, 2009, 2010 Nicira Networks.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -48,11 +48,17 @@ enum stream_state {
 static struct stream_class *stream_classes[] = {
     &tcp_stream_class,
     &unix_stream_class,
+#ifdef HAVE_OPENSSL
+    &ssl_stream_class,
+#endif
 };
 
 static struct pstream_class *pstream_classes[] = {
     &ptcp_pstream_class,
     &punix_pstream_class,
+#ifdef HAVE_OPENSSL
+    &pssl_pstream_class,
+#endif
 };
 
 /* Check the validity of the stream class structures. */
@@ -66,7 +72,8 @@ check_stream_classes(void)
         struct stream_class *class = stream_classes[i];
         assert(class->name != NULL);
         assert(class->open != NULL);
-        if (class->close || class->recv || class->send || class->wait) {
+        if (class->close || class->recv || class->send || class->run
+            || class->run_wait || class->wait) {
             assert(class->close != NULL);
             assert(class->recv != NULL);
             assert(class->send != NULL);
@@ -94,7 +101,8 @@ check_stream_classes(void)
 /* Prints information on active (if 'active') and passive (if 'passive')
  * connection methods supported by the stream. */
 void
-stream_usage(const char *name, bool active, bool passive)
+stream_usage(const char *name, bool active, bool passive,
+             bool bootstrap OVS_UNUSED)
 {
     /* Really this should be implemented via callbacks into the stream
      * providers, but that seems too heavy-weight to bother with at the
@@ -105,6 +113,10 @@ stream_usage(const char *name, bool active, bool passive)
         printf("Active %s connection methods:\n", name);
         printf("  tcp:IP:PORT             "
                "PORT at remote IP\n");
+#ifdef HAVE_OPENSSL
+        printf("  ssl:IP:PORT             "
+               "SSL PORT at remote IP\n");
+#endif
         printf("  unix:FILE               "
                "Unix domain socket named FILE\n");
     }
@@ -113,51 +125,103 @@ stream_usage(const char *name, bool active, bool passive)
         printf("Passive %s connection methods:\n", name);
         printf("  ptcp:PORT[:IP]          "
                "listen to TCP PORT on IP\n");
+#ifdef HAVE_OPENSSL
+        printf("  pssl:PORT[:IP]          "
+               "listen for SSL on PORT on IP\n");
+#endif
         printf("  punix:FILE              "
                "listen on Unix domain socket FILE\n");
     }
+
+#ifdef HAVE_OPENSSL
+    printf("PKI configuration (required to use SSL):\n"
+           "  -p, --private-key=FILE  file with private key\n"
+           "  -c, --certificate=FILE  file with certificate for private key\n"
+           "  -C, --ca-cert=FILE      file with peer CA certificate\n");
+    if (bootstrap) {
+        printf("  --bootstrap-ca-cert=FILE  file with peer CA certificate "
+               "to read or create\n");
+    }
+#endif
 }
 
-/* Attempts to connect a stream to a remote peer.  'name' is a connection name
- * in the form "TYPE:ARGS", where TYPE is an active stream class's name and
- * ARGS are stream class-specific.
- *
- * Returns 0 if successful, otherwise a positive errno value.  If successful,
- * stores a pointer to the new connection in '*streamp', otherwise a null
- * pointer.  */
-int
-stream_open(const char *name, struct stream **streamp)
+/* Given 'name', a stream name in the form "TYPE:ARGS", stores the class
+ * named "TYPE" into '*classp' and returns 0.  Returns EAFNOSUPPORT and stores
+ * a null pointer into '*classp' if 'name' is in the wrong form or if no such
+ * class exists. */
+static int
+stream_lookup_class(const char *name, struct stream_class **classp)
 {
     size_t prefix_len;
     size_t i;
 
-    COVERAGE_INC(stream_open);
     check_stream_classes();
 
-    *streamp = NULL;
+    *classp = NULL;
     prefix_len = strcspn(name, ":");
-    if (prefix_len == strlen(name)) {
+    if (name[prefix_len] == '\0') {
         return EAFNOSUPPORT;
     }
     for (i = 0; i < ARRAY_SIZE(stream_classes); i++) {
         struct stream_class *class = stream_classes[i];
         if (strlen(class->name) == prefix_len
             && !memcmp(class->name, name, prefix_len)) {
-            struct stream *stream;
-            char *suffix_copy = xstrdup(name + prefix_len + 1);
-            int retval = class->open(name, suffix_copy, &stream);
-            free(suffix_copy);
-            if (!retval) {
-                assert(stream->state != SCS_CONNECTING
-                       || stream->class->connect);
-                *streamp = stream;
-            }
-            return retval;
+            *classp = class;
+            return 0;
         }
     }
     return EAFNOSUPPORT;
 }
 
+/* Returns 0 if 'name' is a stream name in the form "TYPE:ARGS" and TYPE is
+ * a supported stream type, otherwise EAFNOSUPPORT.  */
+int
+stream_verify_name(const char *name)
+{
+    struct stream_class *class;
+    return stream_lookup_class(name, &class);
+}
+
+/* Attempts to connect a stream to a remote peer.  'name' is a connection name
+ * in the form "TYPE:ARGS", where TYPE is an active stream class's name and
+ * ARGS are stream class-specific.
+ *
+ * Returns 0 if successful, otherwise a positive errno value.  If successful,
+ * stores a pointer to the new connection in '*streamp', otherwise a null
+ * pointer.  */
+int
+stream_open(const char *name, struct stream **streamp)
+{
+    struct stream_class *class;
+    struct stream *stream;
+    char *suffix_copy;
+    int error;
+
+    COVERAGE_INC(stream_open);
+
+    /* Look up the class. */
+    error = stream_lookup_class(name, &class);
+    if (!class) {
+        goto error;
+    }
+
+    /* Call class's "open" function. */
+    suffix_copy = xstrdup(strchr(name, ':') + 1);
+    error = class->open(name, suffix_copy, &stream);
+    free(suffix_copy);
+    if (error) {
+        goto error;
+    }
+
+    /* Success. */
+    *streamp = stream;
+    return 0;
+
+error:
+    *streamp = NULL;
+    return error;
+}
+
 int
 stream_open_block(const char *name, struct stream **streamp)
 {
@@ -166,6 +230,8 @@ stream_open_block(const char *name, struct stream **streamp)
 
     error = stream_open(name, &stream);
     while (error == EAGAIN) {
+        stream_run(stream);
+        stream_run_wait(stream);
         stream_connect_wait(stream);
         poll_block();
         error = stream_connect(stream);
@@ -312,6 +378,28 @@ stream_send(struct stream *stream, const void *buffer, size_t n)
             : (stream->class->send)(stream, buffer, n));
 }
 
+/* Allows 'stream' to perform maintenance activities, such as flushing
+ * output buffers. */
+void
+stream_run(struct stream *stream)
+{
+    if (stream->class->run) {
+        (stream->class->run)(stream);
+    }
+}
+
+/* Arranges for the poll loop to wake up when 'stream' needs to perform
+ * maintenance activities. */
+void
+stream_run_wait(struct stream *stream)
+{
+    if (stream->class->run_wait) {
+        (stream->class->run_wait)(stream);
+    }
+}
+
+/* Arranges for the poll loop to wake up when 'stream' is ready to take an
+ * action of the given 'type'. */
 void
 stream_wait(struct stream *stream, enum stream_wait_type wait)
 {
@@ -348,42 +436,83 @@ stream_send_wait(struct stream *stream)
     stream_wait(stream, STREAM_SEND);
 }
 
-/* Attempts to start listening for remote stream connections.  'name' is a
- * connection name in the form "TYPE:ARGS", where TYPE is an passive stream
- * class's name and ARGS are stream class-specific.
- *
- * Returns 0 if successful, otherwise a positive errno value.  If successful,
- * stores a pointer to the new connection in '*pstreamp', otherwise a null
- * pointer.  */
-int
-pstream_open(const char *name, struct pstream **pstreamp)
+/* Given 'name', a pstream name in the form "TYPE:ARGS", stores the class
+ * named "TYPE" into '*classp' and returns 0.  Returns EAFNOSUPPORT and stores
+ * a null pointer into '*classp' if 'name' is in the wrong form or if no such
+ * class exists. */
+static int
+pstream_lookup_class(const char *name, struct pstream_class **classp)
 {
     size_t prefix_len;
     size_t i;
 
     check_stream_classes();
 
-    *pstreamp = NULL;
+    *classp = NULL;
     prefix_len = strcspn(name, ":");
-    if (prefix_len == strlen(name)) {
+    if (name[prefix_len] == '\0') {
         return EAFNOSUPPORT;
     }
     for (i = 0; i < ARRAY_SIZE(pstream_classes); i++) {
         struct pstream_class *class = pstream_classes[i];
         if (strlen(class->name) == prefix_len
             && !memcmp(class->name, name, prefix_len)) {
-            char *suffix_copy = xstrdup(name + prefix_len + 1);
-            int retval = class->listen(name, suffix_copy, pstreamp);
-            free(suffix_copy);
-            if (retval) {
-                *pstreamp = NULL;
-            }
-            return retval;
+            *classp = class;
+            return 0;
         }
     }
     return EAFNOSUPPORT;
 }
 
+/* Returns 0 if 'name' is a pstream name in the form "TYPE:ARGS" and TYPE is
+ * a supported pstream type, otherwise EAFNOSUPPORT.  */
+int
+pstream_verify_name(const char *name)
+{
+    struct pstream_class *class;
+    return pstream_lookup_class(name, &class);
+}
+
+/* Attempts to start listening for remote stream connections.  'name' is a
+ * connection name in the form "TYPE:ARGS", where TYPE is an passive stream
+ * class's name and ARGS are stream class-specific.
+ *
+ * Returns 0 if successful, otherwise a positive errno value.  If successful,
+ * stores a pointer to the new connection in '*pstreamp', otherwise a null
+ * pointer.  */
+int
+pstream_open(const char *name, struct pstream **pstreamp)
+{
+    struct pstream_class *class;
+    struct pstream *pstream;
+    char *suffix_copy;
+    int error;
+
+    COVERAGE_INC(pstream_open);
+
+    /* Look up the class. */
+    error = pstream_lookup_class(name, &class);
+    if (!class) {
+        goto error;
+    }
+
+    /* Call class's "open" function. */
+    suffix_copy = xstrdup(strchr(name, ':') + 1);
+    error = class->listen(name, suffix_copy, &pstream);
+    free(suffix_copy);
+    if (error) {
+        goto error;
+    }
+
+    /* Success. */
+    *pstreamp = pstream;
+    return 0;
+
+error:
+    *pstreamp = NULL;
+    return error;
+}
+
 /* Returns the name that was used to open 'pstream'.  The caller must not
  * modify or free the name. */
 const char *
@@ -422,6 +551,27 @@ pstream_accept(struct pstream *pstream, struct stream **new_stream)
     return retval;
 }
 
+/* Tries to accept a new connection on 'pstream'.  If successful, stores the
+ * new connection in '*new_stream' and returns 0.  Otherwise, returns a
+ * positive errno value.
+ *
+ * pstream_accept_block() blocks until a connection is ready or until an error
+ * occurs.  It will not return EAGAIN. */
+int
+pstream_accept_block(struct pstream *pstream, struct stream **new_stream)
+{
+    int error;
+
+    while ((error = pstream_accept(pstream, new_stream)) == EAGAIN) {
+        pstream_wait(pstream);
+        poll_block();
+    }
+    if (error) {
+        *new_stream = NULL;
+    }
+    return error;
+}
+
 void
 pstream_wait(struct pstream *pstream)
 {
@@ -455,6 +605,7 @@ stream_init(struct stream *stream, struct stream_class *class,
                     : SCS_DISCONNECTED);
     stream->error = connect_status;
     stream->name = xstrdup(name);
+    assert(stream->state != SCS_CONNECTING || class->connect);
 }
 
 void