Files
2026-08-21 01:30:26 +09:00

129 lines
3.8 KiB
C

/*
* a minimal HTTP client that speaks over SCTP, for the http-over-sctp test.
*
* curl has no SCTP support, so the harness cannot reach an sctp listener with
* the tool it uses for everything else. this does the least that proves the
* path: open a one-to-one sctp association, send a request, print the status
* code from the reply.
*
* usage: sctpget <ipaddr:port> <path> [body]
*
* it prints the three-digit status code on success, or with 'body' the response
* body instead. checking the body matters: the status line is sent before the
* body is produced, so a task that fails halfway still answers 200 and only the
* body shows it.
*
* it prints "nosctp" if the system cannot make an sctp socket at all, and
* "refused" if nothing is listening - which is what a library built without
* --enable-sctp looks like from out here, since the kernel may well have sctp
* while hio was told not to use it. the caller treats both as a skip. anything
* else prints "error", which is a failure.
*
* plain POSIX sockets on purpose - nothing here should depend on the library
* under test.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main (int argc, char* argv[])
{
char host[64], * colon;
struct sockaddr_in sa;
struct timeval tv;
char req[512], buf[2048];
int fd, n, total = 0;
int want_body;
if (argc < 3 || argc > 4)
{
fprintf (stderr, "usage: %s <ipaddr:port> <path> [body]\n", argv[0]);
return 2;
}
want_body = (argc == 4 && strcmp(argv[3], "body") == 0);
snprintf (host, sizeof(host), "%s", argv[1]);
colon = strrchr(host, ':');
if (!colon) { printf("error\n"); return 2; }
*colon = '\0';
memset (&sa, 0, sizeof(sa));
sa.sin_family = AF_INET;
sa.sin_port = htons((unsigned short)atoi(colon + 1));
if (inet_pton(AF_INET, host, &sa.sin_addr) != 1) { printf("error\n"); return 2; }
/* IPPROTO_SCTP with SOCK_STREAM is the one-to-one style: an association
* behaves like a tcp connection, which is why the server side needs no
* changes beyond being told to listen this way. */
fd = socket(AF_INET, SOCK_STREAM, IPPROTO_SCTP);
if (fd <= -1)
{
/* no kernel support, or the module is not loaded */
printf ("nosctp\n");
return 3;
}
/* keep a stuck server from hanging the whole test run */
tv.tv_sec = 10; tv.tv_usec = 0;
setsockopt (fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
setsockopt (fd, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
if (connect(fd, (struct sockaddr*)&sa, sizeof(sa)) <= -1)
{
/* nothing listening. the kernel has sctp - the socket above was made -
* so this says the server did not bind it, i.e. hio has no sctp
* support. distinguishable from a genuine failure, so the caller can
* skip on this one alone. */
printf ("%s\n", (errno == ECONNREFUSED)? "refused": "error");
close (fd);
return 1;
}
n = snprintf(req, sizeof(req),
"GET %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n", argv[2], argv[1]);
if (send(fd, req, n, 0) != n)
{
printf ("error\n");
close (fd);
return 1;
}
/* the status line is all this needs, but read until the peer is done so a
* half-read response cannot be mistaken for a truncated one */
while (total < (int)sizeof(buf) - 1)
{
n = recv(fd, &buf[total], sizeof(buf) - 1 - total, 0);
if (n <= 0) break;
total += n;
}
buf[total] = '\0';
close (fd);
if (total < 12 || memcmp(buf, "HTTP/1.", 7) != 0)
{
printf ("error\n");
return 1;
}
if (want_body)
{
/* everything past the blank line that ends the header block */
char* p = strstr(buf, "\r\n\r\n");
if (!p) { printf("error\n"); return 1; }
fputs (p + 4, stdout);
return 0;
}
/* "HTTP/1.1 200 ..." - the code starts at offset 9 */
printf ("%.3s\n", &buf[9]);
return 0;
}