Aaro Saila před 2 dny
rodič
revize
9c2c6458a9
8 změnil soubory, kde provedl 323 přidání a 1 odebrání
  1. 34 0
      ch3/GetAddrInfo.c
  2. 39 0
      ch3/TCPClientUtility.c
  3. 3 0
      ch3/TCPClientUtility.h
  4. 56 0
      ch3/TCPEchoClient.c
  5. 26 0
      ch3/meson.build
  6. 158 0
      common/Practical.c
  7. 5 0
      common/Practical.h
  8. 2 1
      meson.build

+ 34 - 0
ch3/GetAddrInfo.c

@@ -0,0 +1,34 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include "Practical.h"
+
+int main(int argc, char **argv) { 
+    if (argc != 3) {
+        DieWithUserMessage("Parameter(s)", "<Address/Name> <Port/Service>");
+    }
+
+    char *addr_string = argv[1];
+    char *port_string = argv[2];
+
+    struct addrinfo addr_criteria = {0};
+    addr_criteria.ai_family = AF_UNSPEC;
+    addr_criteria.ai_socktype = SOCK_STREAM;
+    addr_criteria.ai_protocol = proto_get_num("tcp");
+
+    struct addrinfo *addr_list = nullptr;
+    int res = getaddrinfo(addr_string, port_string, &addr_criteria, &addr_list);
+    if (res != 0) {
+        DieWithUserMessage("getaddrinfo() failed", gai_strerror(res));
+    }
+
+    for (struct addrinfo *addr = addr_list; addr != nullptr; addr = addr->ai_next) {
+        PrintSocketAddress(addr->ai_addr, stdout);
+        fputc('\n', stdout);
+    }
+
+    freeaddrinfo(addr_list);
+
+    return 0;
+}

+ 39 - 0
ch3/TCPClientUtility.c

@@ -0,0 +1,39 @@
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <unistd.h>
+
+#include "Practical.h"
+
+int setup_tcp_client_socket(const char *host, const char *service) {
+    struct addrinfo addr_criteria = {0};
+    addr_criteria.ai_family = AF_UNSPEC;
+    addr_criteria.ai_socktype = SOCK_STREAM;
+    addr_criteria.ai_protocol = proto_get_num("tcp");
+
+    struct addrinfo *serv_addr = nullptr;
+    int ret = getaddrinfo(host, service, &addr_criteria, &serv_addr);
+    if (ret != 0) {
+        DieWithUserMessage("getaddrinfo() failed", gai_strerror(ret));
+    }
+
+    int sock = -1;
+    for (struct addrinfo *addr = serv_addr; addr != nullptr; addr = addr->ai_next) {
+        sock = socket(AF_to_PF(addr->ai_family), addr->ai_socktype, addr->ai_protocol);
+        if (sock < 0) {
+            continue;
+        }
+
+        ret = connect(sock, addr->ai_addr, addr->ai_addrlen);
+        if (ret == 0) {
+            break;
+        } else {
+            close(sock);
+            sock = -1;
+        }
+    }
+
+    freeaddrinfo(serv_addr);
+
+    return sock;
+}

+ 3 - 0
ch3/TCPClientUtility.h

@@ -0,0 +1,3 @@
+#pragma once
+
+int setup_tcp_client_socket(const char *host, const char *service);

+ 56 - 0
ch3/TCPEchoClient.c

@@ -0,0 +1,56 @@
+#include <string.h>
+#include <unistd.h>
+
+#include "TCPClientUtility.h"
+#include "Practical.h"
+
+int main(int argc, char **argv) {
+    if (argc < 3 || argc > 4) {
+        DieWithUserMessage(
+            "Parameter(s)",
+            "<Server Address/Name> <Echo Word> [<Server Port/Service>]"
+        );
+    }
+
+    char *server = argv[1];
+    char *echo_str = argv[2];
+    char *service = (argc == 4) ? argv[3] : "echo";
+
+    int sock = setup_tcp_client_socket(server, service);
+    if (sock < 0) {
+        DieWithUserMessage(
+            "setup_tcp_client_socket() failed",
+            "Unable to connect"
+        );
+    }
+
+    const size_t echo_str_len = strlen(echo_str);
+
+    ssize_t num_bytes = send(sock, echo_str, echo_str_len, 0);
+    if (num_bytes < 0) {
+        DieWithSystemMessage("send() failed");
+    } else if ((size_t) num_bytes != echo_str_len) {
+        DieWithUserMessage("send()", "Sent unexpected amount of bytes");
+    }
+
+    unsigned int total_bytes_rcvd = 0;
+    fputs("Received: ", stdout);
+    while (total_bytes_rcvd < echo_str_len) {
+        char buf[BUFSIZE];
+        num_bytes = recv(sock, buf, BUFSIZE - 1, 0);
+        if (num_bytes < 0) {
+            DieWithSystemMessage("recv() failed");
+        } else if (num_bytes == 0) {
+            DieWithUserMessage("recv()", "Connection closed prematurely");
+        }
+        total_bytes_rcvd += num_bytes;
+        buf[num_bytes] = '\0';
+        fputs(buf, stdout);
+    }
+
+    fputc('\n', stdout);
+
+    close(sock);
+
+    return 0;
+}

+ 26 - 0
ch3/meson.build

@@ -0,0 +1,26 @@
+get_addr_info = executable(
+  'get_addr_info',
+  'GetAddrInfo.c',
+  common_src,
+  include_directories: [
+    common_include
+  ],
+  c_args: [
+    c_args,
+    '-D_POSIX_C_SOURCE=200112L' # For getaddrinfo
+  ]
+)
+
+tcp_echo_client = executable(
+  'tcp_echo_client',
+  'TCPEchoClient.c',
+  'TCPClientUtility.c',
+  common_src,
+  include_directories: [
+    common_include
+  ],
+  c_args: [
+    c_args,
+    '-D_POSIX_C_SOURCE=200112L' # For getaddrinfo
+  ]
+)

+ 158 - 0
common/Practical.c

@@ -1,5 +1,8 @@
+#include <stdio.h>
 #include <netdb.h>
+#include <netinet/in.h>
 #include <stddef.h>
+#include <arpa/inet.h>
 
 #include "Practical.h"
 
@@ -10,3 +13,158 @@ int proto_get_num(const char *name) {
     }
     return protoent->p_proto;
 }
+
+void PrintSocketAddress(const struct sockaddr *address, FILE *stream) {
+    if (address == nullptr || stream == nullptr) {
+        return;
+    }
+
+    void *numericAddress = nullptr;
+    char addrBuffer[INET6_ADDRSTRLEN];
+    in_port_t port;
+    switch (address->sa_family) {
+    case AF_INET:
+        const struct sockaddr_in *const inet_addr = (struct sockaddr_in *) address;
+        numericAddress = (void *) &inet_addr->sin_addr;
+        port = ntohs(inet_addr->sin_port);
+        break;
+    case AF_INET6:
+        const struct sockaddr_in6 *const inet6_addr = (struct sockaddr_in6 *) address;
+        numericAddress = (void *) &inet6_addr->sin6_addr;
+        port = ntohs(inet6_addr->sin6_port);
+        break;
+    default:
+        fputs("[unkown type]", stream);
+        return;
+    }
+
+    if (inet_ntop(
+            address->sa_family,
+            numericAddress,
+            addrBuffer,
+            sizeof(addrBuffer)
+        )
+        != nullptr) {
+        fprintf(stream, "%s", addrBuffer);
+        if (port != 0) {
+            fprintf(stream, "-%u", port);
+        }
+    } else {
+        fputs("[invalid address]", stream);
+    }
+}
+
+// Mentioned as a possible exercise in 3.2.1
+int AF_to_PF(const int af) {
+    switch (af) {
+    case AF_UNSPEC:
+        return PF_UNSPEC;
+    case AF_LOCAL:
+        return PF_LOCAL;
+    case AF_INET:
+        return PF_INET;
+    case AF_AX25:
+        return PF_AX25;
+    case AF_IPX:
+        return PF_IPX;
+    case AF_APPLETALK:
+        return PF_APPLETALK;
+    case AF_NETROM:
+        return PF_NETROM;
+    case AF_BRIDGE:
+        return PF_BRIDGE;
+    case AF_ATMPVC:
+        return PF_ATMPVC;
+    case AF_X25:
+        return PF_X25;
+    case AF_INET6:
+        return PF_INET6;
+    case AF_ROSE:
+        return PF_ROSE;
+    case AF_DECnet:
+        return PF_DECnet;
+    case AF_NETBEUI:
+        return PF_NETBEUI;
+    case AF_SECURITY:
+        return PF_SECURITY;
+    case AF_KEY:
+        return PF_KEY;
+    case AF_PACKET:
+        return PF_PACKET;
+    case AF_ASH:
+        return PF_ASH;
+    case AF_ECONET:
+        return PF_ECONET;
+    case AF_ATMSVC:
+        return PF_ATMSVC;
+    case AF_RDS:
+        return PF_RDS;
+    case AF_SNA:
+        return PF_SNA;
+    case AF_IRDA:
+        return PF_IRDA;
+    case AF_PPPOX:
+        return PF_PPPOX;
+    case AF_WANPIPE:
+        return PF_WANPIPE;
+    case AF_LLC:
+        return PF_LLC;
+    case AF_IB:
+        return PF_IB;
+    case AF_MPLS:
+        return PF_MPLS;
+    case AF_CAN:
+        return PF_CAN;
+    case AF_TIPC:
+        return PF_TIPC;
+    case AF_BLUETOOTH:
+        return PF_BLUETOOTH;
+    case AF_IUCV:
+        return PF_IUCV;
+    case AF_RXRPC:
+        return PF_RXRPC;
+    case AF_ISDN:
+        return PF_ISDN;
+    case AF_PHONET:
+        return PF_PHONET;
+    case AF_IEEE802154:
+        return PF_IEEE802154;
+    case AF_CAIF:
+        return PF_CAIF;
+    case AF_ALG:
+        return PF_ALG;
+    case AF_NFC:
+        return PF_NFC;
+    case AF_VSOCK:
+        return PF_VSOCK;
+    case AF_KCM:
+        return PF_KCM;
+    case AF_QIPCRTR:
+        return PF_QIPCRTR;
+    case AF_SMC:
+        return PF_SMC;
+    case AF_XDP:
+        return PF_XDP;
+    case AF_MCTP:
+        return PF_MCTP;
+    case AF_MAX:
+        return PF_MAX;
+    }
+
+    // Handle entries that (currently) have the same value
+    if (af == AF_UNIX) {
+        return PF_UNIX;
+    }
+    if (af == AF_FILE) {
+        return PF_FILE;
+    }
+    if (af == AF_NETLINK) {
+        return PF_NETLINK;
+    }
+    if (af == AF_ROUTE) {
+        return PF_ROUTE;
+    }
+
+    DieWithUserMessage("AF_to_PF", "Unkown AF_ value");
+    return -1; // Unreachable, makes LSP shut up about not returning a value.
+}

+ 5 - 0
common/Practical.h

@@ -1,7 +1,12 @@
 #pragma once
 
+#include <stdio.h>
+#include <sys/socket.h>
+
 #define BUFSIZE 512
 
 void DieWithUserMessage(const char *msg, const char *detail);
 void DieWithSystemMessage(const char *msg);
 int proto_get_num(const char *name);
+void PrintSocketAddress(const struct sockaddr *address, FILE *stream);
+int AF_to_PF(const int af);

+ 2 - 1
meson.build

@@ -7,7 +7,7 @@ project(
 )
 
 c_args = [
-  '-std=c99',
+  '-std=c23',
   '-Wall',
   '-Wextra',
   '-pedantic'
@@ -23,3 +23,4 @@ common_include = [
 ]
 
 subdir('ch2')
+subdir('ch3')