--- init.c 2006/06/07 17:47:40 1.1 +++ init.c 2006/07/07 19:05:39 @@ -2453,6 +2453,8 @@ c->options.management_echo_buffer_size, c->options.management_state_buffer_size, c->options.management_hold, + c->options.management_http, + c->options.management_http_home, c->options.management_client, c->options.management_write_peer_info_file)) { --- manage.c 2006/06/02 16:51:30 1.1 +++ manage.c 2006/07/12 17:41:28 @@ -52,6 +52,187 @@ #define MANAGEMENT_ECHO_FLAGS 0 #endif + +#define MAN_PFX_GOOD "SUCCESS: " /* success report prefix */ +#define MAN_PFX_FAIL "ERROR: " /* error report prefix */ +#define MAN_END_TEXT "END\r\n" /* indicates end of text */ + + +#define ENABLE_MANAGEMENT_HTTP ENABLE_MANAGEMENT /* XXX */ + +#ifdef ENABLE_MANAGEMENT_HTTP +#define MAN_HTTP 1 +#endif + +#ifdef MAN_HTTP + +/* + * Allow a web brower to connect to the management interface. + * + * If "management-http" is configured, the management interface acts + * as a very simple http server. Management interface commands are + * expected to be embedded in simple HTTP-format GET or POST messages. + * Management interface replies are wrapped in HTML. + * + * Also included here is code to provide a default basic "home page" + * that offers links to launching management interface commands. + * + * Real-time messages are not available due to the nature of HTTP. + * + * Compiles/runs on: FreeBSD 5.4, RedHat Linux 2.6.13 + * Browsers tested: Lynx, Mozilla, Opera, MSIE. + * + * Paul Kern, CNS, UToronto. 2006/07/12 + * Based on a suggestion by Richard Sanford, CNS, UToronto. + * + * ***** ***** ***** ***** ***** ***** ***** + * + * some examples: + * -------------- + * + * 1) the HTTP request ... + * + * | GET /manage?echo=on HTTP/1.1 + * | Http-header1: .... + * | Http-header... + * [ ... etc, etc ... ] + * + * ... translates to the management command ... + * echo on + * + * + * 2) the HTTP submission ... + * + * | POST /manage HTTP/1.1 + * | Http-header1: .... + * | Http-header2: .... + * | Http-header... + * | + * | command=username&Auth=jack&command=password+Auth&+=easypass + * + * ... translates to two management commands ... + * + * username Auth jack + * password Auth easypass + * + * + * 3) the following HTTP requests ... + * + * GET / HTTP/1.1 + * - or - + * GET /manage?default=nnnn.... HTTP/1.1 + * + * ... result in a check for pending conditions (special states). + * + * Any pending special-state messages (eg. waiting for hold release) + * are sent in reply at this time. If there are no pending messages, + * then the reply contains either the built-in "home page" or a link + * to the alternate home URL configured with "management-http-home". + * + * The "default=" request is used as a way to provide + * unique URLs so as to foil caching by some web browsers (eg. lynx). + * + * + * miscellaneous: + * -------------- + * + * - requests for "favicon.ico" cause an HTTP 404 error to be sent in reply. + * + * - requests for "index.htm" or "index.html" cause the built-in "home page" + * to be sent in reply. + * + */ + +#define DEBUG_http 0 /* */ +/* #define DEBUG_http 1 /* */ + +#define MAN_HTTP_GET "GET /" /* http GET command */ +#define MAN_HTTP_POST "POST /" /* http POST command */ +#define MAN_HTTP_ident "manage" /* http management URL id */ +#define MAN_POST_delim "command" /* indicate start of a mgmt cmd */ + +#define MAN_HTTP_ERR_MSG "HTTP/1.0 404 Bad\r\nContent-Length:0\r\nConnection: close\r\nContent-Type: text/html\r\n\r\n" + + +#define SEND_HTTP_COLLECT 0x001 /* collect mgmt output until ready */ +#define SEND_HTTP_READY 0x002 /* ready to send to mgmt client */ +#define SEND_HTML_RAWTEXT 0x004 /* output is not HTML */ + +#define HT_wr_ready(a) do { \ + (a)->connection.ht_state &= ~(SEND_HTTP_COLLECT|SEND_HTML_RAWTEXT); \ + (a)->connection.ht_state |= SEND_HTTP_READY; } while(0) + +#define RECV_HTTP_GET 0x010 /* mgmt cmd arrived via http GET */ +#define RECV_HTTP_POST 0x020 /* mgmt cmd(s) arrived via http POST */ + +#define HT_in_POST(a) ((a)->connection.ht_state & RECV_HTTP_POST) +#define HT_in_GET(a) ((a)->connection.ht_state & RECV_HTTP_GET) +#define HT_now_POST(a) do { (a)->connection.ht_state |= RECV_HTTP_POST; } while(0) +#define HT_now_GET(a) do { (a)->connection.ht_state |= RECV_HTTP_GET; } while(0) + +#define RECV_HTTP_header 0x100 +#define RECV_HTTP_body 0x200 + +#define HT_in_new(a) (((a)->connection.ht_state & (RECV_HTTP_header|RECV_HTTP_body)) == 0) + +#define HT_in_hdr(a) ((a)->connection.ht_state & RECV_HTTP_header) +#define HT_in_bdy(a) ((a)->connection.ht_state & RECV_HTTP_body) + +#define HT_now_hdr(a) do { (a)->connection.ht_state |= RECV_HTTP_header; } while(0) +#define HT_now_bdy(a) do { (a)->connection.ht_state |= RECV_HTTP_body; } while(0) + +#define RECV_HTTP_reset 0x00f +#define HT_now_reset(a) do { (a)->connection.ht_state &= RECV_HTTP_reset; } while(0) + + +#define HTSRV_INDEX "index.htm" /* a default http URL */ +#define HTSRV_ICON "favicon.ico" /* a common browser request */ +#define HTSRV_dflt "default" /* default web command. */ + + + /* frequently used HTML fragments. */ +#define HTML_HREF "" +#define HTML_SUBMIT "" + +#define HTML_BUTTON " ", + HTML_BUTTON "needok\" value=\"token-insertion-request cancel\"> Cancel ", + "
", + NULL +}; + +static char *net_html[] = { + "net", + "[MS-Windows only]
", + "Display " MAN_self_link "?net\">IP information returned by the MS-Windows API.
", + NULL +}; + +static char *kill_html[] = { + "kill", + "[servers only]
", + "Kill a client instance by its ...
", + MAN_self_form "\">Common-name or IP address:port " HTML_GETSTR "kill\">. " HTML_DO_IT, + NULL +}; + + +/* + * HTML command description table. + */ +char **man_html_tab[] = { + log_html, + echo_html, + state_html, + hold_html, + mute_html, + verb_html, + signal_html, + status_html, + username_html, + password_html, + userpass_html, + authretry_html, + net_html, + kill_html, + needok_html, + (char **)NULL +}; + + +/* + * parse a line like... + * GET /manage HTTP/1.1 + * GET /manage?echo=on HTTP/1.1 + * GET /manage?echo=all HTTP/1.1 + * POST /manage HTTP/1.1 + */ +static void +man_parse_http(struct management *man, const unsigned char **ret) +{ + struct command_line *cl = man->connection.in; + unsigned char *s = NULL, *p; + int len; + + *ret = NULL; + + s = BSTR (&cl->buf); + + if (strncmp(s, MAN_HTTP_GET, (len = strlen(MAN_HTTP_GET))) == 0) + { + s += len; + HT_now_GET(man); + } + else if (strncmp(s, MAN_HTTP_POST, (len = strlen(MAN_HTTP_POST))) == 0) + { + s += len; + HT_now_POST(man); + } + else if (!HT_in_bdy(man)) + return; + + len = strlen(MAN_HTTP_ident); + if (strncmp(s, MAN_HTTP_ident, len) == 0) s += len; + + if (*s == '?') s++; /* skip past first '?' */ + + for (p = s ; *p ; p++ ) + { + if (*p == ' ') + HT_now_hdr(man); + if (*p == ' ') + { + /* + * end of useful GET. + * ignore proto-ID string (follows 2nd sp). + */ + *p = '\0'; + break; + } + + /* XXX - simplistic parsing. */ + if (*p == '=' || *p == '+' || *p == '&') + *p = ' '; + + } + + if (*s != '\0') + *ret = s; +} + +static void +man_html_index (struct management *man) +{ + int i, j, n, buflen, maxlen = 0; + char *buf; + + /* + * display management commands in 2 parts: + * - present the more basic commands in the first part. + * - present a more complete list of commands in the second part. + */ + + /* + * part 1 - basic (most frequently used?) commands. + */ + HT_push(man, "

Basic OpenVPN management commands:


\n"); + + /* + * part 2 - list of all available management commands. + */ + /* find longest command name for buffer sizing. */ + for (i = 0 ; man_html_tab[i] ; i++ ) + { + n = strlen(man_html_tab[i][0]); + if (n > maxlen) maxlen = n; + } + buflen = (3 * maxlen) + 32; + buf = gc_malloc( buflen, 0, NULL); + + /* + * present all available commands. + * start with an index jump-to list. + */ + HT_push(man, "


Available OpenVPN management commands:

\n"); + + /* show all commands and their options. */ + for (i = 0 ; man_html_tab[i] ; i++ ) + { + (void) openvpn_snprintf(buf, buflen, + "
%s
\n", + man_html_tab[i][0], man_html_tab[i][0]); + HT_push(man, buf); + + for (j = 1 ; man_html_tab[i][j] ; j++) + HT_push(man, man_html_tab[i][j]); + + HT_push(man, "
\n"); + } + + HT_push(man, HTML_END); + HT_flush(man); + + free(buf); +} +#endif /* MAN_HTTP */ + static int man_read (struct management *man) { /* * read command line from socket */ - unsigned char buf[256]; + unsigned char buf[MGMTBUFSZ]; int len = 0; len = recv (man->connection.sd_cli, buf, sizeof (buf), MSG_NOSIGNAL); @@ -1111,7 +1710,12 @@ { man_reset_client_socket (man, false); } - else if (len > 0) + else if ( len < 0 ) + { + if (man_io_error (man, "recv")) + man_reset_client_socket (man, false); + } + else /* len > 0 */ { bool processed_command = false; @@ -1123,6 +1727,115 @@ */ output_list_reset (man->connection.out); +#ifdef MAN_HTTP + if (man->settings.man_http) + { + const unsigned char *line; + const unsigned char *htline = NULL; + int linelen; + + while ((line = command_line_get (man->connection.in))) + { + linelen = strlen(line); +#if DEBUG_http +msg(D_MANAGEMENT_DEBUG, "MANAGEMENT: HTTP: 0x%03x [%d,%d] line '%s'", man->connection.ht_state, len, linelen, line); +#endif + if (HT_in_hdr(man) && linelen == 0) + { + HT_now_bdy(man); + command_line_next (man->connection.in); + command_line_add (man->connection.in, "\n", 1); +#if DEBUG_http +msg(D_MANAGEMENT_DEBUG, "MANAGEMENT: HTTP: 0x%03x now body", man->connection.ht_state); +#endif + continue; + } + +/* + * GET - run command, ignore remaining input. + * POST - flush to command line (command line follows an empty line). + */ + + htline = NULL; + if ((HT_in_POST(man) && HT_in_bdy(man)) || HT_in_new(man)) + { + man_parse_http (man, &htline); +#if DEBUG_http +msg(D_MANAGEMENT_DEBUG, "MANAGEMENT: HTTP: 0x%03x parse = '%s'", man->connection.ht_state, htline); +#endif + } + +#if DEBUG_http +msg(D_MANAGEMENT_DEBUG, "MANAGEMENT: HTTP: 0x%03x", man->connection.ht_state); +#endif + + if (HT_in_GET(man) && + (htline == NULL || strncmp(htline, HTSRV_dflt, strlen(HTSRV_dflt)) == 0)) + { + /* + * an empty (or default) command. + * - send the special state msg + * or send a "home" link, + * or send an index html page. + */ + HT_wr_ready(man); + if (man->persist.special_state_msg) + { + char *fmt = "%s

Management interface " HTML_HREF "%s\">summary.
%s"; + char *link = HTSRV_INDEX; + + if (man->settings.man_http_home) + { + fmt = "%s

" HTML_HREF "%s\">Home.
%s"; + link = man->settings.man_http_home; + } + msg(M_CLIENT, fmt, + man->persist.special_state_msg, link, HTML_END); + } + else if (man->settings.man_http_home) + { + msg(M_CLIENT, "
" HTML_HREF "%s\">Home.
%s", + man->settings.man_http_home, HTML_END); + } + else + man_html_index(man); + break; + } + if ((htline != NULL) && (HT_in_bdy(man) || HT_in_GET(man))) + { + HT_wr_ready(man); + line = string_alloc(htline, NULL); + + if (strncmp(line, HTSRV_INDEX, strlen(HTSRV_INDEX)) == 0) + { + man_html_index(man); + break; + } + if (strncmp(line, HTSRV_ICON, strlen(HTSRV_ICON)) == 0) + { + msg(D_MANAGEMENT_DEBUG, "MANAGEMENT: HTTP: ignore '%s'", line); + (void) send (man->connection.sd_cli, MAN_HTTP_ERR_MSG, strlen(MAN_HTTP_ERR_MSG), MSG_NOSIGNAL); + man_reset_client_socket (man, false); + break; + } + + msg(D_MANAGEMENT, "MANAGEMENT: HTTP: command '%s'", line); + man_process_command (man, (char *) line); + if (man->connection.halt) + break; + processed_command = true; + + if (HT_in_GET(man)) + break; + } + + command_line_next (man->connection.in); + } +#if DEBUG_http +msg(D_MANAGEMENT_DEBUG, "MANAGEMENT: HTTP: 0x%03x [%d,%d] proc '%d'", man->connection.ht_state, len, linelen, processed_command); +#endif + } +#else /* MAN_HTTP */ /* * process command line if complete */ @@ -1137,6 +1850,7 @@ processed_command = true; } } +#endif /* MAN_HTTP */ /* * Reset output state to MS_CC_WAIT_(READ|WRITE) @@ -1153,25 +1867,130 @@ man_update_io_state (man); } } - else /* len < 0 */ + return len; +} + +#ifdef MAN_HTTP +/* + * terminate a plain-text block. append a link to return "home". + */ +static void +man_end_rawtext(struct management *man) +{ + if (man->settings.man_http_home) { - if (man_io_error (man, "recv")) - man_reset_client_socket (man, false); + /* use a config-supplied URL as the return link. */ + + output_list_push (man->persist.htout, "
" HTML_HREF ); + output_list_push (man->persist.htout, man->settings.man_http_home); + output_list_push (man->persist.htout, "\">Home" HTML_END ); + } + else + { + /* add a generic URL as the return link. */ + + struct gc_arena gc = gc_new (); + struct buffer out; + + output_list_push (man->persist.htout, + "
" MAN_self_link "?" HTSRV_dflt "=" ); + + /* add a timestamp to foil caching by browsers (ie. lynx). */ + out = alloc_buf_gc (64, &gc); + buf_printf (&out, "%d", (int)time(NULL)); + output_list_push (man->persist.htout, BSTR (&out)); + + output_list_push (man->persist.htout, "\">Next" HTML_END ); + + gc_free (&gc); } - return len; } +#endif /* MAN_HTTP */ static int man_write (struct management *man) { - const int max_send = 256; + const int max_send = MGMTBUFSZ; int sent = 0; const struct buffer *buf = output_list_peek (man->connection.out); + +#ifdef MAN_HTTP + msg(D_MANAGEMENT_DEBUG, "MANAGEMENT: HTTP: wr [%d]'%s'", BLEN(buf), BPTR(buf)); + if (man->settings.man_http && buf && BLEN (buf)) + { + int len = min_int (max_send, BLEN (buf)); + char *bp = string_alloc (BPTR(buf), NULL); + + output_list_push (man->persist.htout, bp); + + sent = len; + output_list_advance (man->connection.out, sent); + + msg(D_MANAGEMENT_DEBUG, "MANAGEMENT: HTTP: http 0x%03x size %d tot %d", man->connection.ht_state, man->persist.htout->size, output_list_count (man->persist.htout)); + + if ((man->connection.ht_state & SEND_HTTP_COLLECT) && + (man->connection.ht_state & SEND_HTTP_READY) && + (man->connection.ht_state & SEND_HTML_RAWTEXT) && + strcmp(bp, MAN_END_TEXT) == 0) + { + man_end_rawtext(man); + + /* reply to browser */ + man->connection.ht_state |= SEND_HTTP_READY; + man->connection.ht_state &= ~SEND_HTTP_COLLECT; + man->connection.ht_state &= ~SEND_HTML_RAWTEXT; + } + + if (man->connection.ht_state && + (man->connection.ht_state & SEND_HTTP_COLLECT) == 0) + { + char ht_hdr[MGMTBUFSZ * 2]; + + if (strncmp(bp, MAN_PFX_GOOD, strlen(MAN_PFX_GOOD)) == 0 || + strncmp(bp, MAN_PFX_FAIL, strlen(MAN_PFX_FAIL)) == 0) + { + man_end_rawtext(man); + } + + /* set the HTTP header */ + openvpn_snprintf(ht_hdr, sizeof(ht_hdr), +"HTTP/1.0 200 OK\r\n\ +Content-Type: text/html\r\n\ +Content-Length: %d\r\n\ +Connection: close\r\n\ +Pragma: no-cache\r\n\ +Cache-Control: no-cache, no-store\r\n\ +\r\n", + output_list_count (man->persist.htout)); + + output_list_prepend (man->persist.htout, ht_hdr); + + buf = output_list_peek(man->persist.htout); + while (buf && BLEN(buf)) + { + len = min_int (max_send, BLEN (buf)); + sent = send (man->connection.sd_cli, BPTR(buf), len, MSG_NOSIGNAL); + if (sent < 0 && man_io_error (man, "send")) break; + + output_list_advance (man->persist.htout, sent); + buf = output_list_peek(man->persist.htout); + } + man->connection.ht_state |= SEND_HTTP_COLLECT; /* not READY */ + man->connection.ht_state &= ~SEND_HTTP_READY; + man->connection.ht_state &= ~SEND_HTML_RAWTEXT; + man_reset_client_socket (man, false); + } + + free(bp); + } + else +#endif /* MAN_HTTP */ if (buf && BLEN (buf)) { - const int len = min_int (max_send, BLEN (buf)); - sent = send (man->connection.sd_cli, BPTR (buf), len, MSG_NOSIGNAL); + int len = min_int (max_send, BLEN (buf)); + + sent = send (man->connection.sd_cli, BPTR(buf), len, MSG_NOSIGNAL); if (sent >= 0) { output_list_advance (man->connection.out, sent); @@ -1239,6 +2058,11 @@ man->persist.state = log_history_init (state_buffer_size); mp->defined = true; + + /* + * Initialize http reply buffer. + */ + mp->htout = output_list_new (0); } } @@ -1257,6 +2081,9 @@ if (mp->state) log_history_close (mp->state); + if (mp->htout) + output_list_free (mp->htout); + CLEAR (*mp); } @@ -1271,6 +2098,8 @@ const int echo_buffer_size, const int state_buffer_size, const bool hold, + const int man_http, + const char *man_http_home, const bool connect_as_client, const char *write_peer_info_file) { @@ -1302,6 +2131,13 @@ ms->hold = hold; /* + * Should OpenVPN use http? + * Is there an outside home page URL? + */ + ms->man_http = man_http; + ms->man_http_home = string_alloc (man_http_home, NULL); + + /* * Should OpenVPN connect to management interface as a client * rather than a server? */ @@ -1343,6 +2179,7 @@ static void man_settings_close (struct man_settings *ms) { + free (ms->man_http_home); free (ms->write_peer_info_file); CLEAR (*ms); } @@ -1365,7 +2202,7 @@ * Allocate helper objects for command line input and * command output from/to the socket. */ - man->connection.in = command_line_new (256); + man->connection.in = command_line_new (MGMTBUFSZ); man->connection.out = output_list_new (0); /* @@ -1435,6 +2272,8 @@ const int echo_buffer_size, const int state_buffer_size, const bool hold, + const int man_http, + const char *man_http_home, const bool connect_as_client, const char *write_peer_info_file) { @@ -1454,6 +2293,8 @@ echo_buffer_size, state_buffer_size, hold, + man_http, + man_http_home, connect_as_client, write_peer_info_file); @@ -1599,7 +2440,12 @@ void management_auth_failure (struct management *man, const char *type) { - msg (M_CLIENT, ">PASSWORD:Verification Failed: '%s'", type); +#ifdef MAN_HTTP + if (man->settings.man_http) + msg (M_CLIENT, "
PASSWORD: Verification Failed: '%s'\n
", type); + else +#endif /* MAN_HTTP */ + msg (M_CLIENT, ">PASSWORD:Verification Failed: '%s'", type); } static inline bool @@ -1926,7 +2772,7 @@ { volatile int signal_received = 0; const bool standalone_disabled_save = man->persist.standalone_disabled; - struct buffer alert_msg = alloc_buf_gc (128, &gc); + struct buffer alert_msg = alloc_buf_gc (MGMTBUFSZ, &gc); const char *alert_type = NULL; const char *prefix = NULL; unsigned int up_query_mode = 0; @@ -1955,13 +2801,61 @@ prefix = "PASSWORD"; alert_type = "username/password"; } - buf_printf (&alert_msg, ">%s:Need '%s' %s", + +#ifdef MAN_HTTP + if (man->settings.man_http) + { + const char *focus = NULL, *form = "query"; + + if (up_query_mode == UP_QUERY_USER_PASS || up_query_mode == UP_QUERY_PASS) + buf_printf (&alert_msg, + HTML_FOCUS_fmt "\r\n", + form, type); + + buf_printf (&alert_msg, +"
%s:Need '%s' %s
\r\n" +MAN_self_form "%s\" method=\"post\">\r\n", + prefix, type, alert_type, form); + + if (up_query_mode == UP_QUERY_USER_PASS) + { + buf_printf (&alert_msg, +HTML_HIDE_POST "username\">\r\n [%s] Username: " +HTML_GETSTR "%s\">
\r\n" +HTML_HIDE_POST "password %s\">\r\n [%s] Password: " +HTML_GETPWD " \">
\r\n " +HTML_SUBMIT "\r\n", + type, type, type, type); + } + + if (up_query_mode == UP_QUERY_PASS) + { + buf_printf (&alert_msg, +HTML_HIDE_POST "password\">\r\n [%s] Password: " +HTML_GETPWD "%s\">
\r\n " +HTML_SUBMIT "\r\n", + type, type); + } + + if (up_query_mode == UP_QUERY_NEED_OK) + { + buf_printf (&alert_msg, +" " HTML_BUTTON "needok\" value=\"%s ok\">Okay" +" " HTML_BUTTON "needok\" value=\"%s cancel\">Cancel ", + type, type); + } + } + else +#endif /* MAN_HTTP */ + { + buf_printf (&alert_msg, ">%s:Need '%s' %s", prefix, type, alert_type); - if (flags & GET_USER_PASS_NEED_OK) - buf_printf (&alert_msg, " MSG:%s", up->username); + if (flags & GET_USER_PASS_NEED_OK) + buf_printf (&alert_msg, " MSG:%s", up->username); + } man_wait_for_client_connection (man, &signal_received, 0, MWCC_PASSWORD_WAIT); if (signal_received) @@ -1970,7 +2864,10 @@ if (ret) { man->persist.special_state_msg = BSTR (&alert_msg); - msg (M_CLIENT, "%s", man->persist.special_state_msg); +#ifdef MAN_HTTP + if (!man->settings.man_http) +#endif /* MAN_HTTP */ + msg (M_CLIENT, "%s\n", man->persist.special_state_msg); /* tell command line parser which info we need */ man->connection.up_query_mode = up_query_mode; @@ -2052,8 +2949,17 @@ if (!signal_received) { - man->persist.special_state_msg = ">HOLD:Waiting for hold release"; - msg (M_CLIENT, "%s", man->persist.special_state_msg); +#ifdef MAN_HTTP + if (man->settings.man_http) + { + man->persist.special_state_msg = "
Waiting for hold release. Start-up OpenVPN " MAN_self_link "?hold=release\">now." ; + } + else +#endif /* MAN_HTTP */ + { + man->persist.special_state_msg = ">HOLD:Waiting for hold release"; + msg (M_CLIENT, "%s", man->persist.special_state_msg); + } /* run command processing event loop until we get our username/password */ do @@ -2183,6 +3089,22 @@ ol->size = 0; } +static void +output_list_prepend (struct output_list *ol, const unsigned char *str) +{ + if (!ol->max_size || ol->size < ol->max_size) + { + struct output_entry *e; + ALLOC_OBJ_CLEAR (e, struct output_entry); + + ++ol->size; + if (ol->head) + e->next = ol->head; + e->buf = string_alloc_buf ((const char *) str, NULL); + ol->head = e; + } +} + void output_list_push (struct output_list *ol, const unsigned char *str) { @@ -2241,6 +3163,21 @@ if (!BLEN (buf)) output_list_pop (ol); } +} + +static int +output_list_count (struct output_list *ol) +{ + int n = 0; + struct output_entry *e = ol->head; + + while (e) + { + n += e->buf.len; + e = e->next; + } + + return n; } /* --- manage.h 2006/06/05 17:14:51 1.1 +++ manage.h 2006/07/07 19:05:40 @@ -190,6 +190,8 @@ counter_type bytes_in; counter_type bytes_out; + + struct output_list *htout; }; struct man_settings { @@ -203,6 +205,8 @@ int state_buffer_size; bool server; bool hold; + bool man_http; + char *man_http_home; bool connect_as_client; char *write_peer_info_file; }; @@ -221,6 +225,7 @@ struct man_connection { int state; + int ht_state; socket_descriptor_t sd_top; socket_descriptor_t sd_cli; @@ -273,6 +278,8 @@ const int echo_buffer_size, const int state_buffer_size, const bool hold, + const int man_http, + const char *man_http_home, const bool connect_as_client, const char *write_peer_info_file); --- openvpn.8 2006/07/05 18:30:04 1.1 +++ openvpn.8 2006/07/07 19:05:40 @@ -180,6 +180,8 @@ [\ \fB\-\-management\-log\-cache\fR\ \fIn\fR\ ] [\ \fB\-\-management\-query\-passwords\fR\ ] [\ \fB\-\-management\fR\ \fIIP\ port\ [pw\-file]\fR\ ] +[\ \fB\-\-management\-http\fR\ ] +[\ \fB\-\-management\-http\-home\fR\ \fIURL\ ] [\ \fB\-\-max\-clients\fR\ \fIn\fR\ ] [\ \fB\-\-max\-routes\-per\-client\fR\ \fIn\fR\ ] [\ \fB\-\-mktun\fR\ ] @@ -2308,6 +2310,17 @@ .B n lines of log file history for usage by the management channel. +.\"********************************************************* +.TP +.B --management-http +Expect management commands to be sent in HTTP format so that +a web browser can be used to connect to the management interface. +.\"********************************************************* +.TP +.B --management-http-home URL +Configure an alternate home URL (instead of the default URL +which points to a basic hard-coded HTML page) when connecting +to the management interface with a web browser. .\"********************************************************* .TP .B --plugin module-pathname [init-string] --- options.c 2006/06/07 17:45:26 1.1 +++ options.c 2006/07/07 19:05:40 @@ -1195,6 +1195,8 @@ SHOW_INT (management_echo_buffer_size); SHOW_BOOL (management_query_passwords); SHOW_BOOL (management_hold); + SHOW_BOOL (management_http); + SHOW_STR (management_http_home); SHOW_BOOL (management_client); SHOW_STR (management_write_peer_info_file); #endif @@ -1524,6 +1526,7 @@ #ifdef ENABLE_MANAGEMENT if (!options->management_addr && (options->management_query_passwords || options->management_hold + || options->management_http || options->management_http_home || options->management_client || options->management_write_peer_info_file || options->management_log_history_cache != defaults.management_log_history_cache)) msg (M_USAGE, "--management is not specified, however one or more options which modify the behavior of --management were specified"); @@ -3159,6 +3162,17 @@ { VERIFY_PERMISSION (OPT_P_GENERAL); options->management_hold = true; + } + else if (streq (p[0], "management-http")) + { + VERIFY_PERMISSION (OPT_P_GENERAL); + options->management_http = true; + } + else if (streq (p[0], "management-http-home")) + { + VERIFY_PERMISSION (OPT_P_GENERAL); + options->management_http = true; + options->management_http_home = p[1]; } else if (streq (p[0], "management-client")) { --- options.h 2006/06/07 17:47:05 1.1 +++ options.h 2006/07/07 19:05:40 @@ -281,6 +281,8 @@ int management_state_buffer_size; bool management_query_passwords; bool management_hold; + bool management_http; + const char *management_http_home; bool management_client; const char *management_write_peer_info_file; #endif