ciscodump: fix line parsing and fix memleak.
[metze/wireshark/wip.git] / extcap / ciscodump.c
1 /* ciscodump.c
2  * ciscodump is extcap tool used to capture data using a ssh on a remote cisco router
3  *
4  * Copyright 2015, Dario Lombardo
5  *
6  * Wireshark - Network traffic analyzer
7  * By Gerald Combs <gerald@wireshark.org>
8  * Copyright 1998 Gerald Combs
9  *
10  * This program is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU General Public License
12  * as published by the Free Software Foundation; either version 2
13  * of the License, or (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23  */
24
25 #include "config.h"
26
27 #include <extcap/extcap-base.h>
28 #include <wsutil/interface.h>
29 #include <extcap/ssh-base.h>
30 #include <writecap/pcapio.h>
31
32 #include <errno.h>
33 #include <string.h>
34 #include <fcntl.h>
35
36 #define CISCODUMP_VERSION_MAJOR "1"
37 #define CISCODUMP_VERSION_MINOR "0"
38 #define CISCODUMP_VERSION_RELEASE "0"
39
40 /* The read timeout in msec */
41 #define CISCODUMP_READ_TIMEOUT 3000
42
43 #define CISCODUMP_EXTCAP_INTERFACE "cisco"
44 #define SSH_READ_BLOCK_SIZE 1024
45 #define SSH_READ_TIMEOUT 10000
46
47 #define WIRESHARK_CAPTURE_POINT "WIRESHARK_CAPTURE_POINT"
48 #define WIRESHARK_CAPTURE_BUFFER "WIRESHARK_CAPTURE_BUFFER"
49 #define WIRESHARK_CAPTURE_ACCESSLIST "WIRESHARK_CAPTURE_ACCESSLIST"
50
51 #define PCAP_SNAPLEN 0xffff
52
53 #define PACKET_MAX_SIZE 65535
54
55 #define MINIMUM_IOS_MAJOR 12
56 #define MINIMUM_IOS_MINOR 4
57
58 /* Status of the parser */
59 enum {
60         CISCODUMP_PARSER_STARTING,
61         CISCODUMP_PARSER_IN_PACKET,
62         CISCODUMP_PARSER_IN_HEADER,
63         CISCODUMP_PARSER_END_PACKET,
64         CISCODUMP_PARSER_ERROR
65 };
66
67 enum {
68         EXTCAP_BASE_OPTIONS_ENUM,
69         OPT_HELP,
70         OPT_VERSION,
71         OPT_REMOTE_HOST,
72         OPT_REMOTE_PORT,
73         OPT_REMOTE_USERNAME,
74         OPT_REMOTE_PASSWORD,
75         OPT_REMOTE_INTERFACE,
76         OPT_REMOTE_FILTER,
77         OPT_SSHKEY,
78         OPT_SSHKEY_PASSPHRASE,
79         OPT_REMOTE_COUNT
80 };
81
82 static struct option longopts[] = {
83         EXTCAP_BASE_OPTIONS,
84         { "help", no_argument, NULL, OPT_HELP},
85         { "version", no_argument, NULL, OPT_VERSION},
86         SSH_BASE_OPTIONS,
87         { 0, 0, 0, 0}
88 };
89
90 static char* interfaces_list_to_filter(GSList* interfaces, unsigned int remote_port)
91 {
92         GString* filter = g_string_new(NULL);
93         GSList* cur;
94
95         if (interfaces) {
96                 g_string_append_printf(filter, "deny tcp host %s any eq %u, deny tcp any eq %u host %s",
97                                 (char*)interfaces->data, remote_port, remote_port, (char*)interfaces->data);
98                 cur = g_slist_next(interfaces);
99                 while (cur) {
100                         g_string_append_printf(filter, ", deny tcp host %s any eq %u, deny tcp any eq %u host %s",
101                                 (char*)cur->data, remote_port, remote_port, (char*)cur->data);
102                         cur = g_slist_next(cur);
103                 }
104                 g_string_append_printf(filter, ", permit ip any any");
105         }
106
107         return g_string_free(filter, FALSE);
108 }
109
110 static char* local_interfaces_to_filter(const unsigned int remote_port)
111 {
112         GSList* interfaces = local_interfaces_to_list();
113         char* filter = interfaces_list_to_filter(interfaces, remote_port);
114         g_slist_free_full(interfaces, g_free);
115         return filter;
116 }
117
118 /* Read bytes from the channel. If bytes == -1, read all data (until timeout). If outbuf != NULL, data are stored there */
119 static int read_output_bytes(ssh_channel channel, int bytes, char* outbuf)
120 {
121         char chr;
122         int total;
123         int bytes_read;
124
125         total = (bytes > 0 ? bytes : G_MAXINT);
126         bytes_read = 0;
127
128         while(ssh_channel_read_timeout(channel, &chr, 1, 0, 2000) > 0 && bytes_read < total) {
129                 g_debug("%c", chr);
130                 if (chr == '^')
131                         return EXIT_FAILURE;
132                 if (outbuf)
133                         outbuf[bytes_read] = chr;
134                 bytes_read++;
135         }
136         return EXIT_SUCCESS;
137 }
138
139 static void ciscodump_cleanup(ssh_session sshs, ssh_channel channel, const char* iface, const char* cfilter)
140 {
141         if (channel) {
142                 if (read_output_bytes(channel, -1, NULL) == EXIT_SUCCESS) {
143                         ssh_channel_printf(channel, "monitor capture point stop %s\n", WIRESHARK_CAPTURE_POINT);
144                         ssh_channel_printf(channel, "no monitor capture point ip cef %s %s\n", WIRESHARK_CAPTURE_POINT, iface);
145                         ssh_channel_printf(channel, "no monitor capture buffer %s\n", WIRESHARK_CAPTURE_BUFFER);
146                         if (cfilter) {
147                                 ssh_channel_printf(channel, "configure terminal\n");
148                                 ssh_channel_printf(channel, "no ip access-list ex %s\n", WIRESHARK_CAPTURE_ACCESSLIST);
149                         }
150                         read_output_bytes(channel, -1, NULL);
151                 }
152         }
153         ssh_cleanup(&sshs, &channel);
154 }
155
156 static int wait_until_data(ssh_channel channel, const long unsigned count)
157 {
158         long unsigned got = 0;
159         char output[SSH_READ_BLOCK_SIZE];
160         char* output_ptr;
161         guint rounds = 100;
162
163         while (got < count && rounds--) {
164                 if (ssh_channel_printf(channel, "show monitor capture buffer %s parameters\n", WIRESHARK_CAPTURE_BUFFER) == EXIT_FAILURE) {
165                         g_warning("Can't write to channel");
166                         return EXIT_FAILURE;
167                 }
168                 if (read_output_bytes(channel, SSH_READ_BLOCK_SIZE, output) == EXIT_FAILURE)
169                         return EXIT_FAILURE;
170
171                 output_ptr = g_strstr_len(output, strlen(output), "Packets");
172                 if (!output_ptr) {
173                         g_warning("Error in sscanf()");
174                         return EXIT_FAILURE;
175                 } else {
176                         if (sscanf(output_ptr, "Packets : %lu", &got) != 1)
177                                 return EXIT_FAILURE;
178                 }
179         }
180         g_debug("All packets got: dumping");
181         return EXIT_SUCCESS;
182 }
183
184 static int parse_line(char* packet _U_, unsigned* offset, char* line, int status)
185 {
186         char** parts;
187         char** part;
188         guint32 value;
189         size_t size;
190
191         if (strlen(line) <= 1) {
192                 if (status == CISCODUMP_PARSER_IN_PACKET)
193                         return CISCODUMP_PARSER_END_PACKET;
194                 else
195                         return status;
196         }
197
198         /* we got the packet header                                    */
199         /* The packet header is a line like:                           */
200         /* 16:09:37.171 ITA Mar 18 2016 : IPv4 LES CEF    : Gi0/1 None */
201         if (g_regex_match_simple("^\\d{2}:\\d{2}:\\d{2}.\\d+ .*", line, G_REGEX_CASELESS, G_REGEX_MATCH_ANCHORED)) {
202                 return CISCODUMP_PARSER_IN_HEADER;
203         }
204
205         /* we got a line of the packet                                                          */
206         /* A line looks like                                                                    */
207         /* <address>: <1st group> <2nd group> <3rd group> <4th group> <ascii representation>    */
208         /* ABCDEF01: 01020304 05060708 090A0B0C 0D0E0F10 ................                       */
209         /* Note that any of the 4 groups are optional and that a group can be 1 to 4 bytes long */
210         parts = g_regex_split_simple(
211                 "^[\\dA-F]{8,8}:\\s+([\\dA-F]{2,8})\\s+([\\dA-F]{2,8}){0,1}\\s+([\\dA-F]{2,8}){0,1}\\s+([\\dA-F]{2,8}){0,1}.*",
212                 line, G_REGEX_CASELESS, G_REGEX_MATCH_ANCHORED);
213
214         part = parts;
215         while(*part) {
216                 if (strlen(*part) > 1) {
217                         value = (guint32)strtoul(*part, NULL, 16);
218                         value = ntohl(value);
219                         size = strlen(*part) / 2;
220                         memcpy(packet + *offset, &value, size);
221                         *offset += (guint32)size;
222                 }
223                 part++;
224         }
225         g_strfreev(parts);
226         return CISCODUMP_PARSER_IN_PACKET;
227 }
228
229 static void ssh_loop_read(ssh_channel channel, FILE* fp, const long unsigned count)
230 {
231         char line[SSH_READ_BLOCK_SIZE];
232         char chr;
233         unsigned offset = 0;
234         unsigned packet_size = 0;
235         char* packet;
236         time_t curtime = time(NULL);
237         int err;
238         guint64 bytes_written;
239         long unsigned packets = 0;
240         int status = CISCODUMP_PARSER_STARTING;
241
242         /* This is big enough to put on the heap */
243         packet = (char*)g_malloc(PACKET_MAX_SIZE);
244
245         do {
246                 if (ssh_channel_read_timeout(channel, &chr, 1, FALSE, SSH_READ_TIMEOUT) == SSH_ERROR) {
247                         g_warning("Error reading from channel");
248                         g_free(packet);
249                         return;
250                 }
251
252                 if (chr != '\n') {
253                         line[offset] = chr;
254                         offset++;
255                 } else {
256                         /* Parse the current line */
257                         line[offset] = '\0';
258                         status = parse_line(packet, &packet_size, line, status);
259
260                         if (status == CISCODUMP_PARSER_END_PACKET) {
261                                 /* dump the packet to the pcap file */
262                                 libpcap_write_packet(fp, curtime, (guint32)(curtime / 1000), packet_size, packet_size, packet, &bytes_written, &err);
263                                 g_debug("Dumped packet %lu size: %u", packets, packet_size);
264                                 packet_size = 0;
265                                 status = CISCODUMP_PARSER_STARTING;
266                                 packets++;
267                         }
268                         offset = 0;
269                 }
270
271         } while(packets < count);
272
273         g_free(packet);
274 }
275
276 static int check_ios_version(ssh_channel channel)
277 {
278         gchar* cmdline = "show version | include Cisco IOS\n";
279         gchar version[255];
280         guint major = 0;
281         guint minor = 0;
282         gchar* cur;
283
284         memset(version, 0x0, 255);
285
286         if (ssh_channel_write(channel, cmdline, (guint32)strlen(cmdline)) == SSH_ERROR)
287                 return FALSE;
288         if (read_output_bytes(channel, (int)strlen(cmdline), NULL) == EXIT_FAILURE)
289                 return FALSE;
290         if (read_output_bytes(channel, 255, version) == EXIT_FAILURE)
291                 return FALSE;
292
293         cur = g_strstr_len(version, strlen(version), "Version");
294         if (cur) {
295                 cur += strlen("Version ");
296                 if (sscanf(cur, "%u.%u", &major, &minor) != 2)
297                         return FALSE;
298
299                 if ((major > MINIMUM_IOS_MAJOR) || (major == MINIMUM_IOS_MAJOR && minor >= MINIMUM_IOS_MINOR)) {
300                         g_debug("Current IOS Version: %u.%u", major, minor);
301                         if (read_output_bytes(channel, -1, NULL) == EXIT_FAILURE)
302                                 return FALSE;
303                         return TRUE;
304                 }
305         }
306
307         g_warning("Invalid IOS version. Minimum version: 12.4, current: %u.%u", major, minor);
308         return FALSE;
309 }
310
311 static ssh_channel run_capture(ssh_session sshs, const char* iface, const char* cfilter, const unsigned long int count)
312 {
313         char* cmdline = NULL;
314         ssh_channel channel;
315         int ret = 0;
316
317         channel = ssh_channel_new(sshs);
318         if (!channel)
319                 return NULL;
320
321         if (ssh_channel_open_session(channel) != SSH_OK)
322                 goto error;
323
324         if (ssh_channel_request_pty(channel) != SSH_OK)
325                 goto error;
326
327         if (ssh_channel_change_pty_size(channel, 80, 24) != SSH_OK)
328                 goto error;
329
330         if (ssh_channel_request_shell(channel) != SSH_OK)
331                 goto error;
332
333         if (!check_ios_version(channel))
334                 goto error;
335
336         if (ssh_channel_printf(channel, "terminal length 0\n") == EXIT_FAILURE)
337                 goto error;
338
339         if (ssh_channel_printf(channel, "monitor capture buffer %s max-size 9500\n", WIRESHARK_CAPTURE_BUFFER) == EXIT_FAILURE)
340                 goto error;
341
342         if (ssh_channel_printf(channel, "monitor capture buffer %s limit packet-count %lu\n", WIRESHARK_CAPTURE_BUFFER, count) == EXIT_FAILURE)
343                 goto error;
344
345         if (cfilter) {
346                 gchar* multiline_filter;
347                 gchar* chr;
348
349                 if (ssh_channel_printf(channel, "configure terminal\n") == EXIT_FAILURE)
350                         goto error;
351
352                 if (ssh_channel_printf(channel, "ip access-list ex %s\n", WIRESHARK_CAPTURE_ACCESSLIST) == EXIT_FAILURE)
353                         goto error;
354
355                 multiline_filter = g_strdup(cfilter);
356                 chr = multiline_filter;
357                 while((chr = g_strstr_len(chr, strlen(chr), ",")) != NULL) {
358                         chr[0] = '\n';
359                         g_debug("Splitting filter into multiline");
360                 }
361                 ret = ssh_channel_write(channel, multiline_filter, (uint32_t)strlen(multiline_filter));
362                 g_free(multiline_filter);
363                 if (ret == SSH_ERROR)
364                         goto error;
365
366                 if (ssh_channel_printf(channel, "\nend\n") == EXIT_FAILURE)
367                         goto error;
368
369                 if (ssh_channel_printf(channel, "monitor capture buffer %s filter access-list %s\n",
370                                 WIRESHARK_CAPTURE_BUFFER, WIRESHARK_CAPTURE_ACCESSLIST) == EXIT_FAILURE)
371                         goto error;
372         }
373
374         if (ssh_channel_printf(channel, "monitor capture point ip cef %s %s both\n", WIRESHARK_CAPTURE_POINT,
375                         iface) == EXIT_FAILURE)
376                 goto error;
377
378         if (ssh_channel_printf(channel, "monitor capture point associate %s %s \n", WIRESHARK_CAPTURE_POINT,
379                         WIRESHARK_CAPTURE_BUFFER) == EXIT_FAILURE)
380                 goto error;
381
382         if (ssh_channel_printf(channel, "monitor capture point start %s\n", WIRESHARK_CAPTURE_POINT) == EXIT_FAILURE)
383                 goto error;
384
385         if (read_output_bytes(channel, -1, NULL) == EXIT_FAILURE)
386                 goto error;
387
388         if (wait_until_data(channel, count) == EXIT_FAILURE)
389                 goto error;
390
391         if (read_output_bytes(channel, -1, NULL) == EXIT_FAILURE)
392                 goto error;
393
394         cmdline = g_strdup_printf("show monitor capture buffer %s dump\n", WIRESHARK_CAPTURE_BUFFER);
395         if (ssh_channel_printf(channel, cmdline) == EXIT_FAILURE)
396                 goto error;
397
398         if (read_output_bytes(channel, (int)strlen(cmdline), NULL) == EXIT_FAILURE)
399                 goto error;
400
401         g_free(cmdline);
402         return channel;
403 error:
404         g_free(cmdline);
405         g_warning("Error running ssh remote command");
406         read_output_bytes(channel, -1, NULL);
407
408         ssh_channel_close(channel);
409         ssh_channel_free(channel);
410         return NULL;
411 }
412
413 static int ssh_open_remote_connection(const char* hostname, const unsigned int port, const char* username, const char* password,
414         const char* sshkey, const char* sshkey_passphrase, const char* iface, const char* cfilter,
415         const unsigned long int count, const char* fifo)
416 {
417         ssh_session sshs;
418         ssh_channel channel;
419         FILE* fp = stdout;
420         guint64 bytes_written = 0;
421         int err;
422         int ret = EXIT_FAILURE;
423         char* err_info = NULL;
424
425         if (g_strcmp0(fifo, "-")) {
426                 /* Open or create the output file */
427                 fp = fopen(fifo, "w");
428                 if (!fp) {
429                         g_warning("Error creating output file: %s", g_strerror(errno));
430                         return EXIT_FAILURE;
431                 }
432         }
433
434         sshs = create_ssh_connection(hostname, port, username, password, sshkey, sshkey_passphrase, &err_info);
435         if (!sshs) {
436                 g_warning("Error creating connection: %s", err_info);
437                 goto cleanup;
438         }
439
440         if (!libpcap_write_file_header(fp, 1, PCAP_SNAPLEN, FALSE, &bytes_written, &err)) {
441                 g_warning("Can't write pcap file header");
442                 goto cleanup;
443         }
444
445         channel = run_capture(sshs, iface, cfilter, count);
446         if (!channel) {
447                 ret = EXIT_FAILURE;
448                 goto cleanup;
449         }
450
451         /* read from channel and write into fp */
452         ssh_loop_read(channel, fp, count);
453
454         /* clean up and exit */
455         ciscodump_cleanup(sshs, channel, iface, cfilter);
456
457         ret = EXIT_SUCCESS;
458 cleanup:
459         if (fp != stdout)
460                 fclose(fp);
461
462         return ret;
463 }
464
465 static int list_config(char *interface, unsigned int remote_port)
466 {
467         unsigned inc = 0;
468         char* ipfilter;
469
470         if (!interface) {
471                 g_warning("No interface specified.");
472                 return EXIT_FAILURE;
473         }
474
475         if (g_strcmp0(interface, CISCODUMP_EXTCAP_INTERFACE)) {
476                 g_warning("interface must be %s", CISCODUMP_EXTCAP_INTERFACE);
477                 return EXIT_FAILURE;
478         }
479
480         ipfilter = local_interfaces_to_filter(remote_port);
481
482         printf("arg {number=%u}{call=--remote-host}{display=Remote SSH server address}"
483                 "{type=string}{tooltip=The remote SSH host. It can be both "
484                 "an IP address or a hostname}{required=true}\n", inc++);
485         printf("arg {number=%u}{call=--remote-port}{display=Remote SSH server port}"
486                 "{type=unsigned}{default=22}{tooltip=The remote SSH host port (1-65535)}"
487                 "{range=1,65535}\n", inc++);
488         printf("arg {number=%u}{call=--remote-username}{display=Remote SSH server username}"
489                 "{type=string}{default=%s}{tooltip=The remote SSH username. If not provided, "
490                 "the current user will be used}\n", inc++, g_get_user_name());
491         printf("arg {number=%u}{call=--remote-password}{display=Remote SSH server password}"
492                 "{type=password}{tooltip=The SSH password, used when other methods (SSH agent "
493                 "or key files) are unavailable.}\n", inc++);
494         printf("arg {number=%u}{call=--sshkey}{display=Path to SSH private key}"
495                 "{type=fileselect}{tooltip=The path on the local filesystem of the private ssh key}\n",
496                 inc++);
497         printf("arg {number=%u}{call--sshkey-passphrase}{display=SSH key passphrase}"
498                 "{type=password}{tooltip=Passphrase to unlock the SSH private key}\n",
499                 inc++);
500         printf("arg {number=%u}{call=--remote-interface}{display=Remote interface}"
501                 "{type=string}{required=true}{tooltip=The remote network interface used for capture"
502                 "}\n", inc++);
503         printf("arg {number=%u}{call=--remote-filter}{display=Remote capture filter}"
504                 "{type=string}{tooltip=The remote capture filter}", inc++);
505         if (ipfilter)
506                 printf("{default=%s}", ipfilter);
507         printf("\n");
508         printf("arg {number=%u}{call=--remote-count}{display=Packets to capture}"
509                 "{type=unsigned}{required=true}{tooltip=The number of remote packets to capture.}\n",
510                 inc++);
511
512         g_free(ipfilter);
513
514         return EXIT_SUCCESS;
515 }
516
517 int main(int argc, char **argv)
518 {
519         int result;
520         int option_idx = 0;
521         int i;
522         char* remote_host = NULL;
523         unsigned int remote_port = 22;
524         char* remote_username = NULL;
525         char* remote_password = NULL;
526         char* remote_interface = NULL;
527         char* sshkey = NULL;
528         char* sshkey_passphrase = NULL;
529         char* remote_filter = NULL;
530         unsigned long int count = 0;
531         int ret = EXIT_FAILURE;
532         extcap_parameters * extcap_conf = g_new0(extcap_parameters, 1);
533         char* help_header = NULL;
534
535 #ifdef _WIN32
536         WSADATA wsaData;
537
538         attach_parent_console();
539 #endif  /* _WIN32 */
540
541         extcap_base_set_util_info(extcap_conf, argv[0], CISCODUMP_VERSION_MAJOR, CISCODUMP_VERSION_MINOR,
542                 CISCODUMP_VERSION_RELEASE, NULL);
543         extcap_base_register_interface(extcap_conf, CISCODUMP_EXTCAP_INTERFACE, "Cisco remote capture", 147, "Remote capture dependent DLT");
544
545         help_header = g_strdup_printf(
546                 " %s --extcap-interfaces\n"
547                 " %s --extcap-interface=%s --extcap-dlts\n"
548                 " %s --extcap-interface=%s --extcap-config\n"
549                 " %s --extcap-interface=%s --remote-host myhost --remote-port 22222 "
550                 "--remote-username myuser --remote-interface gigabit0/0 "
551                 "--fifo=FILENAME --capture\n", argv[0], argv[0], CISCODUMP_EXTCAP_INTERFACE, argv[0],
552                 CISCODUMP_EXTCAP_INTERFACE, argv[0], CISCODUMP_EXTCAP_INTERFACE);
553         extcap_help_add_header(extcap_conf, help_header);
554         g_free(help_header);
555
556         extcap_help_add_option(extcap_conf, "--help", "print this help");
557         extcap_help_add_option(extcap_conf, "--version", "print the version");
558         extcap_help_add_option(extcap_conf, "--verbose", "print more messages");
559         extcap_help_add_option(extcap_conf, "--remote-host <host>", "the remote SSH host");
560         extcap_help_add_option(extcap_conf, "--remote-port <port>", "the remote SSH port (default: 22)");
561         extcap_help_add_option(extcap_conf, "--remote-username <username>", "the remote SSH username (default: the current user)");
562         extcap_help_add_option(extcap_conf, "--remote-password <password>", "the remote SSH password. "
563                 "If not specified, ssh-agent and ssh-key are used");
564         extcap_help_add_option(extcap_conf, "--sshkey <public key path>", "the path of the ssh key");
565         extcap_help_add_option(extcap_conf, "--sshkey-passphrase <public key passphrase>", "the passphrase to unlock public ssh");
566         extcap_help_add_option(extcap_conf, "--remote-interface <iface>", "the remote capture interface");
567         extcap_help_add_option(extcap_conf, "--remote-filter <filter>", "a filter for remote capture "
568                 "(default: don't capture data for lal interfaces IPs)");
569
570         opterr = 0;
571         optind = 0;
572
573         if (argc == 1) {
574                 extcap_help_print(extcap_conf);
575                 goto end;
576         }
577
578         for (i = 0; i < argc; i++)
579                 g_debug("%s ", argv[i]);
580
581         while ((result = getopt_long(argc, argv, ":", longopts, &option_idx)) != -1) {
582
583                 switch (result) {
584
585                 case OPT_HELP:
586                         extcap_help_print(extcap_conf);
587                         ret = EXIT_SUCCESS;
588                         goto end;
589
590                 case OPT_VERSION:
591                         printf("%s\n", extcap_conf->version);
592                         goto end;
593
594                 case OPT_REMOTE_HOST:
595                         g_free(remote_host);
596                         remote_host = g_strdup(optarg);
597                         break;
598
599                 case OPT_REMOTE_PORT:
600                         remote_port = (unsigned int)strtoul(optarg, NULL, 10);
601                         if (remote_port > 65535 || remote_port == 0) {
602                                 g_warning("Invalid port: %s", optarg);
603                                 goto end;
604                         }
605                         break;
606
607                 case OPT_REMOTE_USERNAME:
608                         g_free(remote_username);
609                         remote_username = g_strdup(optarg);
610                         break;
611
612                 case OPT_REMOTE_PASSWORD:
613                         g_free(remote_password);
614                         remote_password = g_strdup(optarg);
615                         memset(optarg, 'X', strlen(optarg));
616                         break;
617
618                 case OPT_SSHKEY:
619                         g_free(sshkey);
620                         sshkey = g_strdup(optarg);
621                         break;
622
623                 case OPT_SSHKEY_PASSPHRASE:
624                         g_free(sshkey_passphrase);
625                         sshkey_passphrase = g_strdup(optarg);
626                         memset(optarg, 'X', strlen(optarg));
627                         break;
628
629                 case OPT_REMOTE_INTERFACE:
630                         g_free(remote_interface);
631                         remote_interface = g_strdup(optarg);
632                         break;
633
634                 case OPT_REMOTE_FILTER:
635                         g_free(remote_filter);
636                         remote_filter = g_strdup(optarg);
637                         break;
638
639                 case OPT_REMOTE_COUNT:
640                         count = strtoul(optarg, NULL, 10);
641                         break;
642
643                 case ':':
644                         /* missing option argument */
645                         g_warning("Option '%s' requires an argument", argv[optind - 1]);
646                         break;
647
648                 default:
649                         if (!extcap_base_parse_options(extcap_conf, result - EXTCAP_OPT_LIST_INTERFACES, optarg)) {
650                                 g_warning("Invalid option: %s", argv[optind - 1]);
651                                 goto end;
652                         }
653                 }
654         }
655
656         if (optind != argc) {
657                 g_warning("Unexpected extra option: %s", argv[optind]);
658                 goto end;
659         }
660
661         if (extcap_base_handle_interface(extcap_conf)) {
662                 ret = EXIT_SUCCESS;
663                 goto end;
664         }
665
666         if (extcap_conf->show_config) {
667                 ret = list_config(extcap_conf->interface, remote_port);
668                 goto end;
669         }
670
671 #ifdef _WIN32
672         result = WSAStartup(MAKEWORD(1,1), &wsaData);
673         if (result != 0) {
674                 g_warning("ERROR: WSAStartup failed with error: %d", result);
675                 goto end;
676         }
677 #endif  /* _WIN32 */
678
679         if (extcap_conf->capture) {
680                 if (!remote_host) {
681                         g_warning("Missing parameter: --remote-host");
682                         goto end;
683                 }
684
685                 if (!remote_interface) {
686                         g_warning("ERROR: No interface specified (--remote-interface)");
687                         goto end;
688                 }
689                 if (count == 0) {
690                         g_warning("ERROR: count of packets must be specified (--remote-count)");
691                         goto end;
692                 }
693
694                 ret = ssh_open_remote_connection(remote_host, remote_port, remote_username,
695                         remote_password, sshkey, sshkey_passphrase, remote_interface,
696                         remote_filter, count, extcap_conf->fifo);
697         } else {
698                 g_debug("You should not come here... maybe some parameter missing?");
699                 ret = EXIT_FAILURE;
700         }
701
702 end:
703         g_free(remote_host);
704         g_free(remote_username);
705         g_free(remote_password);
706         g_free(remote_interface);
707         g_free(sshkey);
708         g_free(sshkey_passphrase);
709         g_free(remote_filter);
710         extcap_base_cleanup(&extcap_conf);
711         return ret;
712 }
713
714 #ifdef _WIN32
715 int _stdcall
716 WinMain (struct HINSTANCE__ *hInstance,
717         struct HINSTANCE__ *hPrevInstance,
718         char               *lpszCmdLine,
719         int                 nCmdShow)
720 {
721         return main(__argc, __argv);
722 }
723 #endif
724
725 /*
726  * Editor modelines  -  https://www.wireshark.org/tools/modelines.html
727  *
728  * Local variables:
729  * c-basic-offset: 4
730  * tab-width: 4
731  * indent-tabs-mode: t
732  * End:
733  *
734  * vi: set shiftwidth=4 tabstop=4 noexpandtab:
735  * :indentSize=4:tabSize=4:noTabs=false:
736  */