]> Kevux Git Server - fll/commitdiff
Feature: Add f_socket_name_host() and f_socket_name_peer().
authorKevin Day <kevin@kevux.org>
Tue, 20 Jun 2023 02:52:12 +0000 (21:52 -0500)
committerKevin Day <kevin@kevux.org>
Tue, 20 Jun 2023 02:52:12 +0000 (21:52 -0500)
Add missing network socket related functions for getting the "name" of the local or remote connections.

The local name is retuned as a string.

The remote name is returned on the f_socket_t structure.
I considered doing the work necessary to better generalize this but I decided not to.
I can leave that for once I am better experience in the network programming.

13 files changed:
level_0/f_socket/c/socket.c
level_0/f_socket/c/socket.h
level_0/f_socket/c/socket/common.h
level_0/f_socket/data/build/settings-mocks
level_0/f_socket/data/build/settings-tests
level_0/f_socket/tests/unit/c/mock-socket.c
level_0/f_socket/tests/unit/c/mock-socket.h
level_0/f_socket/tests/unit/c/test-socket-name_host.c [new file with mode: 0644]
level_0/f_socket/tests/unit/c/test-socket-name_host.h [new file with mode: 0644]
level_0/f_socket/tests/unit/c/test-socket-name_peer.c [new file with mode: 0644]
level_0/f_socket/tests/unit/c/test-socket-name_peer.h [new file with mode: 0644]
level_0/f_socket/tests/unit/c/test-socket.c
level_0/f_socket/tests/unit/c/test-socket.h

index f4ed967510cdf42dfa620090bb32838b5b942e2f..f7aa11fcec64240616a32af68201d0ab54149a84 100644 (file)
@@ -316,6 +316,62 @@ extern "C" {
   }
 #endif // _di_f_socket_option_set_
 
+#ifndef _di_f_socket_name_host_
+  f_status_t f_socket_name_host(f_socket_t * const socket, f_string_dynamic_t * const name) {
+    #ifndef _di_level_0_parameter_checking_
+      if (!socket) return F_status_set_error(F_parameter);
+      if (!name) return F_status_set_error(F_parameter);
+    #endif // _di_level_0_parameter_checking_
+
+    if (!name->size) {
+      const f_status_t status = f_string_dynamic_resize(F_socket_default_name_max_d, name);
+      if (F_status_is_error(status)) return status;
+    }
+
+    {
+      const int result = gethostname(name->string, (size_t) (name->used ? (name->size - name->used) : name->size));
+
+      if (result < 0) {
+        if (errno == EFAULT) return F_status_set_error(F_buffer);
+        if (errno == EINVAL) return F_status_set_error(F_parameter);
+        if (errno == ENAMETOOLONG) return F_status_set_error(F_string_too_large);
+        if (errno == EPERM) return F_status_set_error(F_prohibited);
+
+        return F_status_set_error(F_failure);
+      }
+    }
+
+    for (; name->used < name->size; ++name->used) {
+      if (!name->string[name->used]) break;
+    } // while
+
+    return F_none;
+  }
+#endif // _di_f_socket_host_name_
+
+#ifndef _di_f_socket_name_peer_
+  f_status_t f_socket_name_peer(f_socket_t * const socket) {
+    #ifndef _di_level_0_parameter_checking_
+      if (!socket) return F_status_set_error(F_parameter);
+    #endif // _di_level_0_parameter_checking_
+
+    const int result = getpeername(socket->id, socket->address, &socket->length);
+
+    if (result < 0) {
+      if (errno == EBADF) return F_status_set_error(F_file_descriptor);
+      if (errno == EFAULT) return F_status_set_error(F_buffer);
+      if (errno == EINVAL) return F_status_set_error(F_parameter);
+      if (errno == ENOBUFS) return F_status_set_error(F_buffer_not);
+      if (errno == ENOTCONN) return F_status_set_error(F_connect_not);
+      if (errno == ENOTSOCK) return F_status_set_error(F_socket_not);
+
+      return F_status_set_error(F_failure);
+    }
+
+    return F_none;
+  }
+#endif // _di_f_socket_name_peer_
+
 #ifndef _di_f_socket_read_
   f_status_t f_socket_read(f_socket_t * const socket, const int flags, void * const buffer, size_t * const length) {
     #ifndef _di_level_0_parameter_checking_
index 03d333da44b002bf9ef694872164a673d8028439..3ee54b03a52b14b8dde0beb072a93db09fdfdbc7 100644 (file)
@@ -396,6 +396,70 @@ extern "C" {
 #endif // _di_f_socket_option_set_
 
 /**
+ * Get the host name from a socket.
+ *
+ * The host is the name of the local connection on the socket.
+ *
+ * @param socket
+ *   The socket structure.
+ *   The socket.id must represent a valid socket file descriptor.
+ *   The socket.size_read is used to represent the buffer size in buffer and must not be larger than the actual size of the buffer.
+ * @param name
+ *   The retrieved host name.
+ *   The name.size is used to determine as the max size.
+ *   If name.size is 0, then a default max (F_socket_default_name_max_d) is used.
+ *
+ * @return
+ *   F_none on success.
+ *
+ *   F_buffer (with error bit) if the buffer is invalid.
+ *   F_parameter (with error bit) if a parameter is invalid.
+ *   F_prohibited (with error bit) if the system does not permit this operation (could be missing CAP_SYS_ADMIN in the appropraite user namespace).
+ *   F_string_too_large (with error bit) if the name is to large for the max size (name.size).
+ *
+ *   F_failure (with error bit) for any other error.
+ *
+ *   Errors (with error bit) from: f_string_dynamic_resize()
+ *
+ * @see gethostname()
+ *
+ * @see f_string_dynamic_resize()
+ */
+#ifndef _di_f_socket_name_host_
+  extern f_status_t f_socket_name_host(f_socket_t * const socket, f_string_dynamic_t * const name);
+#endif // _di_f_socket_host_name_
+
+/**
+ * Get the peer name from a socket.
+ *
+ * The peer is the name of the remote connection on the socket.
+ * The name of the remote connection refers to the connection itself such as the socket file name or the ip address and the port numbers.
+ *
+ * @param socket
+ *   The socket structure.
+ *   The socket.id must represent a valid socket file descriptor.
+ *   The socket.size_read is used to represent the buffer size in buffer and must not be larger than the actual size of the buffer.
+ *   The socket.address is used to store the name of the remote connection.
+ *
+ * @return
+ *   F_none on success.
+ *
+ *   F_file_descriptor (with error bit) if id is an invalid descriptor.
+ *   F_buffer (with error bit) if the buffer is invalid.
+ *   F_buffer_not (with error bit) due to resource restrictions (maps to ENOBUFS).
+ *   F_connect_not (with error bit) if the socket is not connected.
+ *   F_parameter (with error bit) if a parameter is invalid.
+ *   F_socket_not (with error bit) if the id is not a socket descriptor.
+ *
+ *   F_failure (with error bit) for any other error.
+ *
+ * @see getpeername()
+ */
+#ifndef _di_f_socket_name_peer_
+  extern f_status_t f_socket_name_peer(f_socket_t * const socket);
+#endif // _di_f_socket_name_peer_
+
+/**
  * Read from a socket.
  *
  * @param socket
index 05f057d8fcee5e7313cd3302256cec3e26d50f2d..340a0e1086bfbc54b1362a91acac2528e8e35008 100644 (file)
@@ -20,10 +20,12 @@ extern "C" {
  * Provide socket defaults.
  *
  * F_file_default_*:
+ *   - name_max:   Default buffer size for names.
  *   - read_size:  Default read size in bytes.
  *   - write_size: Default write size in bytes.
  */
 #ifndef _di_f_socket_default_d_
+  #define F_socket_default_name_max_d   256
   #define F_socket_default_read_size_d  8192
   #define F_socket_default_write_size_d 8192
 #endif // _di_f_socket_default_d_
index 6b2e0bcbcceacdcedaf5e1ba07cbff51c18dfef8..ddeb71733a1febd8357abfb4a196381f61108752 100644 (file)
@@ -64,6 +64,8 @@ flags -Wl,--wrap=close
 flags -Wl,--wrap=connect
 flags -Wl,--wrap=getsockopt
 flags -Wl,--wrap=listen
+flags -Wl,--wrap=gethostname
+flags -Wl,--wrap=getpeername
 flags -Wl,--wrap=recvfrom
 flags -Wl,--wrap=recvmsg
 flags -Wl,--wrap=sendmsg
index d844cec067611e12a6a4d7fe402eee46df37baba..ca9c4f21a757648fdb398dd37659a2b10f5b4d79 100644 (file)
@@ -25,7 +25,7 @@ build_language c
 build_libraries -lc -lcmocka
 build_libraries-individual -lf_memory -lf_string -lf_type_array -lf_socket
 
-build_sources_program test-socket-accept.c test-socket-bind.c test-socket-bind_local.c test-socket-connect.c test-socket-create.c test-socket-create_pair.c test-socket-disconnect.c test-socket-listen.c test-socket-option_get.c test-socket-option_set.c test-socket-read.c test-socket-read_message.c test-socket-write.c test-socket-write_message.c
+build_sources_program test-socket-accept.c test-socket-bind.c test-socket-bind_local.c test-socket-connect.c test-socket-create.c test-socket-create_pair.c test-socket-disconnect.c test-socket-listen.c test-socket-name_host.c test-socket-name_peer.c test-socket-option_get.c test-socket-option_set.c test-socket-read.c test-socket-read_message.c test-socket-write.c test-socket-write_message.c
 build_sources_program test-socket.c
 
 build_script no
index 5e35fcaca683e4bf8c76abde7f57ac7d1827dd3e..ea3dbf3bb64f37e4397e126a8b63a02202dcfb98 100644 (file)
@@ -56,6 +56,32 @@ int __wrap_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) {
   return 0;
 }
 
+int __wrap_gethostname(char *name, size_t len) {
+
+  const bool failure = mock_type(bool);
+
+  if (failure) {
+    errno = mock_type(int);
+
+    return -1;
+  }
+
+  return 0;
+}
+
+int __wrap_getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen) {
+
+  const bool failure = mock_type(bool);
+
+  if (failure) {
+    errno = mock_type(int);
+
+    return -1;
+  }
+
+  return 0;
+}
+
 int __wrap_getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen) {
 
   const bool failure = mock_type(bool);
index 3ebcbc07ac0e8a59948273cb702ed5e2420af5d3..32d2972c671f2df9ef732d861d458adcc607699a 100644 (file)
@@ -32,6 +32,8 @@ int __wrap_accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
 int __wrap_bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
 int __wrap_close(int fd);
 int __wrap_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
+int __wrap_gethostname(char *name, size_t len);
+int __wrap_getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
 int __wrap_getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
 int __wrap_listen(int sockfd, int backlog);
 ssize_t __wrap_recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen);
diff --git a/level_0/f_socket/tests/unit/c/test-socket-name_host.c b/level_0/f_socket/tests/unit/c/test-socket-name_host.c
new file mode 100644 (file)
index 0000000..79f2ef3
--- /dev/null
@@ -0,0 +1,112 @@
+#include "test-socket.h"
+#include "test-socket-listen.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void test__f_socket_name_host__allocates_default(void **state) {
+
+  f_string_dynamic_t name = f_string_static_t_initialize;
+  f_socket_t socket = f_socket_t_initialize;
+
+  {
+    will_return(__wrap_gethostname, false);
+
+    const f_status_t status = f_socket_name_host(&socket, &name);
+
+    assert_int_equal(status, F_none);
+    assert_int_equal(name.size, F_socket_default_name_max_d);
+  }
+
+  free(name.string);
+}
+
+void test__f_socket_name_host__fails(void **state) {
+
+  f_string_t name_string = "";
+  f_string_static_t name = f_string_static_t_initialize;
+  name.string = name_string;
+  name.used = 0;
+  name.size = 1;
+
+  f_socket_t socket = f_socket_t_initialize;
+
+  int errnos[] = {
+    EFAULT,
+    EINVAL,
+    ENAMETOOLONG,
+    EPERM,
+    mock_errno_generic,
+  };
+
+  f_status_t statuss[] = {
+    F_buffer,
+    F_parameter,
+    F_string_too_large,
+    F_prohibited,
+    F_failure,
+  };
+
+  for (uint8_t i = 0; i < 5; ++i) {
+
+    will_return(__wrap_gethostname, true);
+    will_return(__wrap_gethostname, errnos[i]);
+
+    const f_status_t status = f_socket_name_host(&socket, &name);
+
+    assert_int_equal(status, F_status_set_error(statuss[i]));
+  } // for
+}
+
+void test__f_socket_name_host__parameter_checking(void **state) {
+
+  f_string_t name_string = "";
+  f_string_static_t name = f_string_static_t_initialize;
+  name.string = name_string;
+  name.used = 0;
+  name.size = 1;
+
+  f_socket_t socket = f_socket_t_initialize;
+
+  {
+    const f_status_t status = f_socket_name_host(0, 0);
+
+    assert_int_equal(status, F_status_set_error(F_parameter));
+  }
+
+  {
+    const f_status_t status = f_socket_name_host(&socket, 0);
+
+    assert_int_equal(status, F_status_set_error(F_parameter));
+  }
+
+  {
+    const f_status_t status = f_socket_name_host(0, &name);
+
+    assert_int_equal(status, F_status_set_error(F_parameter));
+  }
+}
+
+void test__f_socket_name_host__works(void **state) {
+
+  f_string_t name_string = "";
+  f_string_static_t name = f_string_static_t_initialize;
+  name.string = name_string;
+  name.used = 0;
+  name.size = 1;
+
+  f_socket_t socket = f_socket_t_initialize;
+
+  {
+    will_return(__wrap_gethostname, false);
+
+    const f_status_t status = f_socket_name_host(&socket, &name);
+
+    assert_int_equal(status, F_none);
+  }
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/level_0/f_socket/tests/unit/c/test-socket-name_host.h b/level_0/f_socket/tests/unit/c/test-socket-name_host.h
new file mode 100644 (file)
index 0000000..e5032b5
--- /dev/null
@@ -0,0 +1,41 @@
+/**
+ * FLL - Level 0
+ *
+ * Project: Socket
+ * API Version: 0.7
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Test the socket project.
+ */
+#ifndef _TEST__F_socket_name_host_h
+#define _TEST__F_socket_name_host_h
+
+/**
+ * Test that function allocates the string size using the default size.
+ *
+ * @see f_socket_name_host()
+ */
+extern void test__f_socket_name_host__allocates_default(void **state);
+
+/**
+ * Test that function fails.
+ *
+ * @see f_socket_name_host()
+ */
+extern void test__f_socket_name_host__fails(void **state);
+
+/**
+ * Test that parameter checking works as expected.
+ *
+ * @see f_socket_name_host()
+ */
+extern void test__f_socket_name_host__parameter_checking(void **state);
+
+/**
+ * Test that function works.
+ *
+ * @see f_socket_name_host()
+ */
+extern void test__f_socket_name_host__works(void **state);
+
+#endif // _TEST__F_socket_name_host_h
diff --git a/level_0/f_socket/tests/unit/c/test-socket-name_peer.c b/level_0/f_socket/tests/unit/c/test-socket-name_peer.c
new file mode 100644 (file)
index 0000000..25a9c42
--- /dev/null
@@ -0,0 +1,67 @@
+#include "test-socket.h"
+#include "test-socket-listen.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void test__f_socket_name_peer__fails(void **state) {
+
+  f_socket_t socket = f_socket_t_initialize;
+
+  int errnos[] = {
+    EBADF,
+    EFAULT,
+    EINVAL,
+    ENOBUFS,
+    ENOTCONN,
+    ENOTSOCK,
+    mock_errno_generic,
+  };
+
+  f_status_t statuss[] = {
+    F_file_descriptor,
+    F_buffer,
+    F_parameter,
+    F_buffer_not,
+    F_connect_not,
+    F_socket_not,
+    F_failure,
+  };
+
+  for (uint8_t i = 0; i < 7; ++i) {
+
+    will_return(__wrap_getpeername, true);
+    will_return(__wrap_getpeername, errnos[i]);
+
+    const f_status_t status = f_socket_name_peer(&socket);
+
+    assert_int_equal(status, F_status_set_error(statuss[i]));
+  } // for
+}
+
+void test__f_socket_name_peer__parameter_checking(void **state) {
+
+  {
+    const f_status_t status = f_socket_name_peer(0);
+
+    assert_int_equal(status, F_status_set_error(F_parameter));
+  }
+}
+
+void test__f_socket_name_peer__works(void **state) {
+
+  f_socket_t socket = f_socket_t_initialize;
+
+  {
+    will_return(__wrap_getpeername, false);
+
+    const f_status_t status = f_socket_name_peer(&socket);
+
+    assert_int_equal(status, F_none);
+  }
+}
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/level_0/f_socket/tests/unit/c/test-socket-name_peer.h b/level_0/f_socket/tests/unit/c/test-socket-name_peer.h
new file mode 100644 (file)
index 0000000..ce2ae93
--- /dev/null
@@ -0,0 +1,34 @@
+/**
+ * FLL - Level 0
+ *
+ * Project: Socket
+ * API Version: 0.7
+ * Licenses: lgpl-2.1-or-later
+ *
+ * Test the socket project.
+ */
+#ifndef _TEST__F_socket_name_peer_h
+#define _TEST__F_socket_name_peer_h
+
+/**
+ * Test that function fails.
+ *
+ * @see f_socket_name_peer()
+ */
+extern void test__f_socket_name_peer__fails(void **state);
+
+/**
+ * Test that parameter checking works as expected.
+ *
+ * @see f_socket_name_peer()
+ */
+extern void test__f_socket_name_peer__parameter_checking(void **state);
+
+/**
+ * Test that function works.
+ *
+ * @see f_socket_name_peer()
+ */
+extern void test__f_socket_name_peer__works(void **state);
+
+#endif // _TEST__F_socket_name_peer_h
index 820723e6ce52d5577faa06a85fa2f344aa4bff92..8fb134ec989e8e9c8146c1930cf4caf9d90d26de 100644 (file)
@@ -48,6 +48,13 @@ int main(void) {
     cmocka_unit_test(test__f_socket_listen__fails),
     cmocka_unit_test(test__f_socket_listen__works),
 
+    cmocka_unit_test(test__f_socket_name_host__allocates_default),
+    cmocka_unit_test(test__f_socket_name_host__fails),
+    cmocka_unit_test(test__f_socket_name_host__works),
+
+    cmocka_unit_test(test__f_socket_name_peer__fails),
+    cmocka_unit_test(test__f_socket_name_peer__works),
+
     cmocka_unit_test(test__f_socket_option_get__fails),
     cmocka_unit_test(test__f_socket_option_get__works),
 
@@ -74,6 +81,8 @@ int main(void) {
       cmocka_unit_test(test__f_socket_create_pair__parameter_checking),
       cmocka_unit_test(test__f_socket_disconnect__parameter_checking),
       cmocka_unit_test(test__f_socket_listen__parameter_checking),
+      cmocka_unit_test(test__f_socket_name_host__parameter_checking),
+      cmocka_unit_test(test__f_socket_name_peer__parameter_checking),
       cmocka_unit_test(test__f_socket_option_get__parameter_checking),
       cmocka_unit_test(test__f_socket_option_set__parameter_checking),
       cmocka_unit_test(test__f_socket_read__parameter_checking),
index 6758d8bfd0081f332a82fc28b574fe3149f03472..d8e56b5213ca2e5dde14445f14735d0fa1c4b78b 100644 (file)
@@ -34,6 +34,8 @@
 #include "test-socket-create_pair.h"
 #include "test-socket-disconnect.h"
 #include "test-socket-listen.h"
+#include "test-socket-name_host.h"
+#include "test-socket-name_peer.h"
 #include "test-socket-option_get.h"
 #include "test-socket-option_set.h"
 #include "test-socket-read.h"