diff --git a/lib/gopher.c b/lib/gopher.c index d1ca440e5..2fab6dabe 100644 --- a/lib/gopher.c +++ b/lib/gopher.c @@ -121,8 +121,8 @@ static CURLcode gopher_do(struct connectdata *conn, bool *done) curl_off_t *bytecount = &data->req.bytecount; char *path = data->state.path; - char *sel; + ssize_t amount, k; *done = TRUE; /* unconditionally */ @@ -149,12 +149,29 @@ static CURLcode gopher_do(struct connectdata *conn, bool *done) return CURLE_OUT_OF_MEMORY; } - result = Curl_sendf(sockfd, conn, "%s\r\n", sel); - - if(result) { + /* We use Curl_write instead of Curl_sendf to make sure the entire buffer + is sent, which could be sizeable with long selectors. */ + k = strlen(sel); + for(;;) { + result = Curl_write(conn, sockfd, sel, k, &amount); + if (CURLE_OK == result) { /* Which may not have written it all! */ + k -= amount; + sel += amount; + if (k < 1) + break; /* but it did write it all */ + } else { + failf(data, "Failed sending Gopher request"); + return result; + } + } + /* We can use Curl_sendf to send the terminal \r\n relatively safely and + save allocing another string/doing another _write loop. */ + result = Curl_sendf(sockfd, conn, "\r\n"); + if (result != CURLE_OK) { failf(data, "Failed sending Gopher request"); return result; } + Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, bytecount, -1, NULL); /* no upload */ return CURLE_OK; diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am index a31b35665..89df9d010 100644 --- a/tests/data/Makefile.am +++ b/tests/data/Makefile.am @@ -19,55 +19,56 @@ EXTRA_DIST = test1 test108 test117 test127 test20 test27 test34 test46 \ test505 test74 test75 test76 test77 test78 test147 test148 test506 test79 \ test80 test81 test82 test83 test84 test85 test86 test87 test507 test149 \ test88 test89 test90 test508 test91 test92 test203 test93 test94 test95 \ - test510 test97 test98 test99 test150 test151 test152 test153 \ - test154 test155 test156 test157 test158 test159 test511 test160 test161 \ - test162 test163 test164 test512 test165 test166 test167 test168 test169 \ - test170 test171 test172 test204 test205 test173 test174 test175 test176 \ - test177 test513 test514 test178 test179 test180 test181 test182 test183 \ - test184 test185 test186 test187 test188 test189 test191 test192 test193 \ - test194 test195 test196 test197 test198 test515 test516 test517 test518 \ - test210 test211 test212 test220 test221 test222 test223 test224 test206 \ - test207 test208 test209 test213 test240 test241 test242 test519 test214 \ - test215 test216 test217 test218 test199 test225 test226 test227 test228 \ - test229 test233 test234 test235 test236 test520 test237 test238 test239 \ - test243 test245 test246 test247 test248 test249 test250 test251 test252 \ - test253 test254 test255 test521 test522 test523 test256 test257 test258 \ - test259 test260 test261 test262 test263 test264 test265 test266 test267 \ - test268 test269 test270 test271 test272 test273 test274 test275 test524 \ - test525 test276 test277 test526 test527 test528 test530 DISABLED test278 \ - test279 test531 test280 test529 test532 test533 test534 test535 test281 \ - test537 test282 test283 test284 test538 test285 test286 test307 test308 \ - test287 test400 test288 test600 test601 test602 test603 test401 test402 \ - test290 test291 test292 test293 test403 test404 test405 test604 test605 \ - test606 test607 test608 test609 test294 test295 test296 test297 test298 \ - test610 test611 test612 test406 test407 test408 test409 test613 test614 \ - test700 test701 test702 test704 test705 test703 test706 test707 test350 \ - test351 test352 test353 test289 test540 test354 test231 test1000 test1001 \ + test510 test97 test98 test99 test150 test151 test152 test153 test154 \ + test155 test156 test157 test158 test159 test511 test160 test161 test162 \ + test163 test164 test512 test165 test166 test167 test168 test169 test170 \ + test171 test172 test204 test205 test173 test174 test175 test176 test177 \ + test513 test514 test178 test179 test180 test181 test182 test183 test184 \ + test185 test186 test187 test188 test189 test191 test192 test193 test194 \ + test195 test196 test197 test198 test515 test516 test517 test518 test210 \ + test211 test212 test220 test221 test222 test223 test224 test206 test207 \ + test208 test209 test213 test240 test241 test242 test519 test214 test215 \ + test216 test217 test218 test199 test225 test226 test227 test228 test229 \ + test233 test234 test235 test236 test520 test237 test238 test239 test243 \ + test245 test246 test247 test248 test249 test250 test251 test252 test253 \ + test254 test255 test521 test522 test523 test256 test257 test258 test259 \ + test260 test261 test262 test263 test264 test265 test266 test267 test268 \ + test269 test270 test271 test272 test273 test274 test275 test524 test525 \ + test276 test277 test526 test527 test528 test530 DISABLED test278 test279 \ + test531 test280 test529 test532 test533 test534 test535 test281 test537 \ + test282 test283 test284 test538 test285 test286 test307 test308 test287 \ + test400 test288 test600 test601 test602 test603 test401 test402 test290 \ + test291 test292 test293 test403 test404 test405 test604 test605 test606 \ + test607 test608 test609 test294 test295 test296 test297 test298 test610 \ + test611 test612 test406 test407 test408 test409 test613 test614 test700 \ + test701 test702 test704 test705 test703 test706 test707 test350 test351 \ + test352 test353 test289 test540 test354 test231 test1000 test1001 \ test1002 test1003 test1004 test1005 test1006 test615 test1007 test541 \ test1010 test1011 test1012 test542 test543 test536 test1008 test1009 \ test2000 test2001 test2002 test2003 test35 test544 test545 test2004 \ test546 test1013 test1014 test1015 test547 test548 test549 test550 \ - test551 test552 test1016 test1017 test1018 test1019 test1020 test553 \ - test1021 test1022 test1023 test309 test616 test617 test618 test619 \ + test551 test552 test1016 test1017 test1018 test1019 test1020 test553 \ + test1021 test1022 test1023 test309 test616 test617 test618 test619 \ test620 test621 test622 test623 test624 test625 test626 test627 test554 \ - test1024 test1025 test555 test1026 test1027 test1028 test1029 test1030 \ + test1024 test1025 test555 test1026 test1027 test1028 test1029 test1030 \ test556 test557 test1031 test628 test629 test630 test631 test632 test1032 \ - test1033 test539 test1034 test1035 test1036 test1037 test1038 test1039 \ + test1033 test539 test1034 test1035 test1036 test1037 test1038 test1039 \ test1040 test1041 test1042 test1043 test1044 test1045 test1046 test1047 \ test1048 test1049 test1050 test1051 test1052 test1053 test1054 test1055 \ test1056 test1057 test1058 test1059 test1060 test1061 test1062 test1063 \ test1064 test1065 test1066 test1067 test1068 test1069 test1070 test1071 \ test1072 test1073 test1074 test1075 test1076 test1077 test1078 test1079 \ - test1080 test1081 test1082 test1083 test1084 test1085 test633 test634 \ - test635 test636 test637 test558 test559 test1086 test1087 test1088 \ - test574 test575 test576 test577 test1113 test1114 \ - test1089 test1090 test1091 test1092 test1093 test1094 test1095 test1096 \ - test1097 test560 test561 test1098 test1099 test562 test563 test1100 \ - test564 test1101 test1102 test1103 test1104 test299 test310 test311 \ - test312 test1105 test565 test800 test1106 test801 test566 test802 test803 \ - test1107 test1108 test1109 test1110 test1111 test1112 test129 test567 \ - test568 test569 test570 test571 test572 test804 test805 test806 test807 \ - test573 test313 test1115 test578 test579 test1116 + test1080 test1081 test1082 test1083 test1084 test1085 test633 test634 \ + test635 test636 test637 test558 test559 test1086 test1087 test1088 \ + test574 test575 test576 test577 test1113 test1114 test1089 test1090 \ + test1091 test1092 test1093 test1094 test1095 test1096 test1097 test560 \ + test561 test1098 test1099 test562 test563 test1100 test564 test1101 \ + test1102 test1103 test1104 test299 test310 test311 test312 test1105 \ + test565 test800 test1106 test801 test566 test802 test803 test1107 \ + test1108 test1109 test1110 test1111 test1112 test129 test567 test568 \ + test569 test570 test571 test572 test804 test805 test806 test807 test573 \ + test313 test1115 test578 test579 test1116 test1200 test1201 test1202 \ + test1203 filecheck: @mkdir test-place; \ diff --git a/tests/data/test1200 b/tests/data/test1200 new file mode 100644 index 000000000..c1f4971c3 --- /dev/null +++ b/tests/data/test1200 @@ -0,0 +1,40 @@ +# Gopher directory fetch + + + +GOPHER +INDEX + + + +# +# Server-side + + +iMenu results error.host 1 +0Selector /bar bar.foo.invalid 70 +. + + + +# Client-side + + +gopher + + +Gopher index + + +gopher://%HOSTIP:%GOPHERPORT + + + +# +# Verify data after the test has been "shot" + + + + + + diff --git a/tests/data/test1201 b/tests/data/test1201 new file mode 100644 index 000000000..071e4551a --- /dev/null +++ b/tests/data/test1201 @@ -0,0 +1,40 @@ +# Gopher selector fetch + + + +GOPHER +SELECTOR + + + +# +# Server-side + + +iMenu results error.host 1 +0Selector /selector/SELECTOR /bar bar.foo.invalid 70 +. + + + +# Client-side + + +gopher + + +Gopher selector + + +gopher://%HOSTIP:%GOPHERPORT/1/selector/SELECTOR + + + +# +# Verify data after the test has been "shot" + + +/selector/SELECTOR + + + diff --git a/tests/data/test1202 b/tests/data/test1202 new file mode 100644 index 000000000..2ab6fcbcb --- /dev/null +++ b/tests/data/test1202 @@ -0,0 +1,41 @@ +# Gopher query fetch + + + +GOPHER +QUERY + + + +# +# Server-side + + +iSearch results error.host 1 +0Query query succeeded /foo foo.bar.invalid 70 +0Selector /the/search/engine /bar bar.foo.invalid 70 +. + + + +# Client-side + + +gopher + + +Gopher query + + +gopher://%HOSTIP:%GOPHERPORT/7/the/search/engine?query%20succeeded + + + +# +# Verify data after the test has been "shot" + + +/the/search/engine query succeeded + + + diff --git a/tests/data/test1203 b/tests/data/test1203 new file mode 100644 index 000000000..d49c3c817 --- /dev/null +++ b/tests/data/test1203 @@ -0,0 +1,41 @@ +# Gopher IPv6 connectivity test + + + +GOPHER-ipv6 +IPv6 +INDEX + + + +# +# Server-side + + +iMenu results error.host 1 +0Selector /bar bar.foo.invalid 70 +. + + + +# Client-side + + +gopher-ipv6 + + +Gopher IPv6 index + + +-g "gopher://%HOSTIP:%GOPHER6PORT" + + + +# +# Verify data after the test has been "shot" + + + + + + diff --git a/tests/ftp.pm b/tests/ftp.pm index 6a46e7651..535075ac5 100644 --- a/tests/ftp.pm +++ b/tests/ftp.pm @@ -181,7 +181,7 @@ sub killsockfilters { my $pidfile; my $pid; - return if($proto !~ /^(ftp|imap|pop3|smtp)$/); + return if($proto !~ /^(ftp|imap|pop3|smtp|gopher)$/); die "unsupported sockfilter: $which" if($which && ($which !~ /^(main|data)$/)); diff --git a/tests/ftpserver.pl b/tests/ftpserver.pl index ed2a83268..03d93341c 100755 --- a/tests/ftpserver.pl +++ b/tests/ftpserver.pl @@ -29,6 +29,9 @@ # protocol per invoke. You need to start mulitple servers to support multiple # protocols simultaneously. # +# The gopher protocol test server also lives here, borrowing some of the +# routines. +# # It is meant to exercise curl, it is not meant to be a fully working # or even very standard compliant server. # @@ -341,6 +344,7 @@ sub senddata { # for the given protocol. References to protocol command callbacks are # stored in 'commandfunc' hash, and text which will be returned to the # client before the command callback runs is stored in 'displaytext'. +# Gopher is not handled here, however (it has a separate routine). # sub protocolsetup { my $proto = $_[0]; @@ -1294,7 +1298,7 @@ while(@ARGV) { } } elsif($ARGV[0] eq '--proto') { - if($ARGV[1] && ($ARGV[1] =~ /^(ftp|imap|pop3|smtp)$/)) { + if($ARGV[1] && ($ARGV[1] =~ /^(ftp|imap|pop3|smtp|gopher)$/)) { $proto = $1; shift @ARGV; } @@ -1421,7 +1425,7 @@ while(1) { &customize(); # read test control instructions - sendcontrol @welcome; + sendcontrol @welcome unless ($proto eq 'gopher'); #remove global variables from last connection if($ftplistparserstate) { @@ -1474,7 +1478,39 @@ while(1) { my $FTPCMD; my $FTPARG; my $full=$_; - if($proto eq "imap") { + + if($proto eq 'gopher') { + # Gopher protocol support lives right here and we handle it + # right here and now, since there will only ever be one selector + # and no other commands. + + my $sel = $_; + my $query = ''; + my @response = (); + ($sel, $query) = split(/\x09/, $_) if (/\x09/); + + if($sel eq 'erifiedserver') { + # NOT verifiedserver, the first character is the item type in + # a Gopher URL! + push(@response, "iWE ROOLZ: $$\x09\x09error.host\x091\r\n"); + } + if (length($query)) { # fake Veronica, gin up search results + push(@response, "iSearch results\x09\x09error.host\x091\r\n"); + push(@response, + "0Query $query\x09/foo\x09foo.bar.invalid\x0970\r\n"); + } else { # fake selector, gin up a menu + push(@response, "iMenu results\x09\x09error.host\x091\r\n"); + } + push(@response, + "0Selector $sel\x09/bar\x09bar.foo.invalid\x0970\r\n"); + push(@response, ".\r\n"); + sendcontrol @response; + + # disconnect the client now, no command. + $FTPCMD = $FTPARG = ''; + print SFWRITE "DISC\n"; + } + elsif($proto eq "imap") { # IMAP is different with its identifier first on the command line unless (m/^([^ ]+) ([^ ]+) (.*)/ || m/^([^ ]+) ([^ ]+)/) { @@ -1550,7 +1586,7 @@ while(1) { } } - if($check) { + if($check && $proto ne 'gopher') { logmsg "$FTPCMD wasn't handled!\n"; sendcontrol "500 $FTPCMD is not dealt with!\r\n"; } diff --git a/tests/runtests.pl b/tests/runtests.pl index 06ff733f6..a3c402ebe 100755 --- a/tests/runtests.pl +++ b/tests/runtests.pl @@ -133,6 +133,8 @@ my $SMTPPORT; # SMTP my $SMTP6PORT; # SMTP IPv6 server port my $RTSPPORT; # RTSP my $RTSP6PORT; # RTSP IPv6 server port +my $GOPHERPORT; # Gopher +my $GOPHER6PORT; # Gopher IPv6 server port my $srcdir = $ENV{'srcdir'} || '.'; my $CURL="../src/curl".exe_ext(); # what curl executable to run on the tests @@ -193,6 +195,7 @@ my $has_idn; # set if libcurl is built with IDN support my $http_ipv6; # set if HTTP server has IPv6 support my $ftp_ipv6; # set if FTP server has IPv6 support my $tftp_ipv6; # set if TFTP server has IPv6 support +my $gopher_ipv6; # set if Gopher server has IPv6 support my $has_ipv6; # set if libcurl is built with IPv6 support my $has_libz; # set if libcurl is built with libz support my $has_getrlimit; # set if system has getrlimit() @@ -329,7 +332,7 @@ sub init_serverpidfile_hash { } } } - for my $proto (('tftp', 'sftp', 'socks', 'ssh', 'rtsp')) { + for my $proto (('tftp', 'sftp', 'socks', 'ssh', 'rtsp', 'gopher')) { for my $ipvnum ((4, 6)) { for my $idnum ((1, 2)) { my $serv = servername_id($proto, $ipvnum, $idnum); @@ -988,7 +991,8 @@ my %protofunc = ('http' => \&verifyhttp, 'ftps' => \&verifyftp, 'tftp' => \&verifyftp, 'ssh' => \&verifyssh, - 'socks' => \&verifysocks); + 'socks' => \&verifysocks, + 'gopher' => \&verifyftp); sub verifyserver { my ($proto, $ipvnum, $idnum, $ip, $port) = @_; @@ -1180,7 +1184,7 @@ sub runhttpsserver { } ####################################################################### -# start the pingpong server (FTP, POP3, IMAP, SMTP) +# start the pingpong server (FTP, POP3, IMAP, SMTP, GOPHER) # sub runpingpongserver { my ($proto, $id, $verbose, $ipv6) = @_; @@ -1211,6 +1215,9 @@ sub runpingpongserver { elsif($proto eq "smtp") { $port = ($ipvnum==6) ? $SMTP6PORT : $SMTPPORT; } + elsif($proto eq "gopher") { + $port = ($ipvnum==6) ? $GOPHER6PORT : $GOPHERPORT; + } else { print STDERR "Unsupported protocol $proto!!\n"; return 0; @@ -1263,6 +1270,7 @@ sub runpingpongserver { $doesntrun{$pidfile} = 1; return (0,0); } + $pid2 = $pid3; if($verbose) { @@ -2039,7 +2047,9 @@ sub checksystem { @sws = `server/sockfilt --version`; if($sws[0] =~ /IPv6/) { # FTP server has ipv6 support! + # and since the Gopher server descends from it, we have it too! $ftp_ipv6 = 1; + $gopher_ipv6 = 1; } } @@ -2098,6 +2108,10 @@ sub checksystem { if($tftp_ipv6) { logmsg sprintf("TFTP-IPv6/%d ", $TFTP6PORT); } + logmsg sprintf("\n* GOPHER/%d ", $GOPHERPORT); + if($gopher_ipv6) { + logmsg sprintf("GOPHER-IPv6/%d", $GOPHERPORT); + } logmsg sprintf("\n* SSH/%d ", $SSHPORT); logmsg sprintf("SOCKS/%d ", $SOCKSPORT); logmsg sprintf("POP3/%d ", $POP3PORT); @@ -2147,6 +2161,8 @@ sub subVariables { $$thing =~ s/%CLIENT6IP/$CLIENT6IP/g; $$thing =~ s/%RTSPPORT/$RTSPPORT/g; $$thing =~ s/%RTSP6PORT/$RTSP6PORT/g; + $$thing =~ s/%GOPHERPORT/$GOPHERPORT/g; + $$thing =~ s/%GOPHER6PORT/$GOPHER6PORT/g; # The purpose of FTPTIME2 and FTPTIME3 is to provide times that can be # used for time-out tests and that whould work on most hosts as these @@ -3211,6 +3227,7 @@ sub startservers { if(($what eq "pop3") || ($what eq "ftp") || ($what eq "imap") || + ($what eq "gopher") || ($what eq "smtp")) { if(!$run{$what}) { ($pid, $pid2) = runpingpongserver($what, "", $verbose); @@ -3242,6 +3259,17 @@ sub startservers { $run{'ftp-ipv6'}="$pid $pid2"; } } + elsif($what eq "gopher-ipv6") { + if(!$run{'gopher-ipv6'}) { + ($pid, $pid2) = runpingpongserver("gopher","",$verbose,"ipv6"); + if($pid <= 0) { + return "failed starting GOPHER-IPv6 server"; + } + logmsg sprintf("* pid gopher-ipv6 => %d %d\n", $pid, + $pid2) if($verbose); + $run{'gopher-ipv6'}="$pid $pid2"; + } + } elsif($what eq "http") { if(!$run{'http'}) { ($pid, $pid2) = runhttpserver($verbose); @@ -3821,6 +3849,8 @@ $SMTPPORT = $base++; $SMTP6PORT = $base++; $RTSPPORT = $base++; $RTSP6PORT = $base++; +$GOPHERPORT =$base++; +$GOPHER6PORT=$base++; ####################################################################### # clear and create logging directory: diff --git a/tests/serverhelp.pm b/tests/serverhelp.pm index 4b3b50545..8429b405a 100644 --- a/tests/serverhelp.pm +++ b/tests/serverhelp.pm @@ -96,7 +96,7 @@ sub servername_str { $proto = uc($proto) if($proto); die "unsupported protocol: $proto" unless($proto && - ($proto =~ /^(((FTP|HTTP|IMAP|POP3|SMTP)S?)|(TFTP|SFTP|SOCKS|SSH|RTSP))$/)); + ($proto =~ /^(((FTP|HTTP|IMAP|POP3|SMTP)S?)|(TFTP|SFTP|SOCKS|SSH|RTSP|GOPHER))$/)); $ipver = (not $ipver) ? 'ipv4' : lc($ipver); die "unsupported IP version: $ipver" unless($ipver && @@ -148,7 +148,8 @@ sub server_pidfilename { sub server_logfilename { my ($logdir, $proto, $ipver, $idnum) = @_; my $trailer = '_server.log'; - $trailer = '_stunnel.log' if(lc($proto) =~ /^(ftp|http|imap|pop3|smtp)s$/); + $trailer = '_stunnel.log' if(lc($proto) =~ /^(ftp|http|imap|pop3|smtp)s$/|| + lc($proto) eq 'gopher'); return "${logdir}/". servername_canon($proto, $ipver, $idnum) ."$trailer"; } @@ -189,7 +190,7 @@ sub server_outputfilename { sub mainsockf_pidfilename { my ($proto, $ipver, $idnum) = @_; die "unsupported protocol: $proto" unless($proto && - (lc($proto) =~ /^(ftp|imap|pop3|smtp)s?$/)); + ((lc($proto) =~ /^(ftp|imap|pop3|smtp)s?$/) || lc($proto) eq 'gopher')); my $trailer = (lc($proto) =~ /^ftps?$/) ? '_sockctrl.pid':'_sockfilt.pid'; return '.'. servername_canon($proto, $ipver, $idnum) ."$trailer"; } @@ -201,7 +202,7 @@ sub mainsockf_pidfilename { sub mainsockf_logfilename { my ($logdir, $proto, $ipver, $idnum) = @_; die "unsupported protocol: $proto" unless($proto && - (lc($proto) =~ /^(ftp|imap|pop3|smtp)s?$/)); + ((lc($proto) =~ /^(ftp|imap|pop3|smtp)s?$/) || lc($proto) eq 'gopher')); my $trailer = (lc($proto) =~ /^ftps?$/) ? '_sockctrl.log':'_sockfilt.log'; return "${logdir}/". servername_canon($proto, $ipver, $idnum) ."$trailer"; }