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.
}
#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_
#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
* 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_
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
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
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);
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);
--- /dev/null
+#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
--- /dev/null
+/**
+ * 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
--- /dev/null
+#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
--- /dev/null
+/**
+ * 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
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),
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),
#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"