mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-07-05 15:00:49 +02:00
Fix emNET support and add tests
The emNET `wolfSSL_LastError` branches were incorrect. The second one was never hit and would never compile. The first one inverts error codes that should not be inverted. This fixes that code and adds a test with a shim layer to test emNET calls without using emNET.
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
name: emNET non-blocking handshake test
|
||||
|
||||
# START OF COMMON SECTION
|
||||
on:
|
||||
push:
|
||||
branches: [ 'master', 'main', 'release/**' ]
|
||||
pull_request:
|
||||
branches: [ '*' ]
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
# END OF COMMON SECTION
|
||||
|
||||
# Build wolfSSL with -DWOLFSSL_EMNET using the clean-room shim in
|
||||
# tests/emnet/ (IP/IP.h + emnet_shim.c), link a non-blocking TLS 1.3
|
||||
# handshake test with -Wl,--wrap=recv,--wrap=send, and run it. The
|
||||
# test exercises the WOLFSSL_EMNET path in wolfSSL_LastError() and
|
||||
# asserts that would-block events surface as WANT_READ/WANT_WRITE and
|
||||
# the handshake completes, guarding against regressions in the emNET
|
||||
# error-translation logic.
|
||||
|
||||
jobs:
|
||||
emnet_nonblock:
|
||||
name: wolfSSL emNET non-blocking handshake
|
||||
if: github.repository_owner == 'wolfssl'
|
||||
runs-on: ubuntu-24.04
|
||||
timeout-minutes: 20
|
||||
steps:
|
||||
- name: Checkout wolfSSL
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install build deps
|
||||
uses: ./.github/actions/install-apt-deps
|
||||
with:
|
||||
packages: autoconf automake libtool build-essential
|
||||
|
||||
- name: Bootstrap
|
||||
run: ./autogen.sh
|
||||
|
||||
- name: Configure wolfSSL (WOLFSSL_EMNET + emNET shim headers)
|
||||
run: |
|
||||
./configure \
|
||||
--enable-static --disable-shared \
|
||||
--enable-tls13 --disable-oldtls \
|
||||
--enable-ecc --disable-examples \
|
||||
CFLAGS="-DWOLFSSL_EMNET -I$(pwd)/tests/emnet"
|
||||
|
||||
- name: Build wolfSSL
|
||||
run: make -j$(nproc)
|
||||
|
||||
- name: Build emNET non-blocking test
|
||||
run: make -C tests/emnet
|
||||
|
||||
- name: Run emNET non-blocking test
|
||||
run: make -C tests/emnet run
|
||||
+5
-5
@@ -151,8 +151,12 @@ static WC_INLINE int wolfSSL_LastError(int err, SOCKET_T sd)
|
||||
return WSAGetLastError();
|
||||
#elif defined(EBSNET)
|
||||
return xn_getlasterror();
|
||||
#elif defined(WOLFSSL_LINUXKM) || defined(WOLFSSL_EMNET)
|
||||
#elif defined(WOLFSSL_LINUXKM)
|
||||
return -err; /* Return provided error value with corrected sign. */
|
||||
#elif defined(WOLFSSL_EMNET)
|
||||
/* emNET BSD sockets return the IP_ERR_* value (negative) directly
|
||||
* from send/recv on failure; no translation needed. */
|
||||
return err;
|
||||
#elif defined(FUSION_RTOS)
|
||||
#include <fclerrno.h>
|
||||
return FCL_GET_ERRNO;
|
||||
@@ -170,10 +174,6 @@ static WC_INLINE int wolfSSL_LastError(int err, SOCKET_T sd)
|
||||
}
|
||||
return err;
|
||||
}
|
||||
#elif defined(WOLFSSL_EMNET)
|
||||
/* Get the real socket error */
|
||||
IP_SOCK_getsockopt(sd, SOL_SOCKET, SO_ERROR, &err, (int)sizeof(old));
|
||||
return err;
|
||||
#else
|
||||
return errno;
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/* IP.h -- clean-room shim for the subset of SEGGER emNET (embOS/IP) API
|
||||
* that wolfSSL's WOLFSSL_EMNET port compiles against. Written from the
|
||||
* public API surface documented in SEGGER UM07001; contains no SEGGER
|
||||
* source.
|
||||
*
|
||||
* Scope: enough to build wolfSSL with -DWOLFSSL_EMNET on a POSIX host
|
||||
* for CI test purposes. Only error constants and IP_SOCK_getsockopt are
|
||||
* provided here; the runtime behaviour of send/recv under emNET is
|
||||
* emulated by emnet_shim.c via linker --wrap.
|
||||
*/
|
||||
|
||||
#ifndef WOLFSSL_EMNET_SHIM_IP_H
|
||||
#define WOLFSSL_EMNET_SHIM_IP_H
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* emNET error codes (UM07001). Values match the public ABI. */
|
||||
#define IP_ERR_CONN_ABORTED (-5)
|
||||
#define IP_ERR_WOULD_BLOCK (-6)
|
||||
#define IP_ERR_CONN_REFUSED (-7)
|
||||
#define IP_ERR_CONN_RESET (-8)
|
||||
#define IP_ERR_PIPE (-13)
|
||||
#define IP_ERR_FAULT (-25)
|
||||
|
||||
/* BSD-style socket option retrieval. Signature matches the SEGGER API:
|
||||
* length is passed by pointer of type int*, unlike POSIX socklen_t*. */
|
||||
int IP_SOCK_getsockopt(int sd, int level, int optname,
|
||||
void *optval, int *optlen);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* WOLFSSL_EMNET_SHIM_IP_H */
|
||||
@@ -0,0 +1,52 @@
|
||||
# Build the emNET non-blocking handshake test.
|
||||
# Requires wolfSSL already configured + built at the repo root with
|
||||
# WOLFSSL_EMNET and the emnet IP include path, e.g.:
|
||||
#
|
||||
# cd $(repo_root)
|
||||
# ./autogen.sh
|
||||
# ./configure --enable-static --disable-shared --enable-tls13 \
|
||||
# --disable-oldtls CFLAGS="-DWOLFSSL_EMNET \
|
||||
# -I$$(pwd)/tests/emnet"
|
||||
# make
|
||||
# make -C tests/emnet run
|
||||
|
||||
CURDIR := $(shell pwd)
|
||||
WOLFSSL_ROOT := $(abspath $(CURDIR)/../..)
|
||||
WOLFSSL_LIB := $(WOLFSSL_ROOT)/src/.libs/libwolfssl.a
|
||||
|
||||
CC ?= cc
|
||||
CFLAGS_TEST := -Wall -Wextra -O2 -g \
|
||||
-DWOLFSSL_EMNET \
|
||||
-I$(CURDIR) \
|
||||
-I$(WOLFSSL_ROOT)
|
||||
LDFLAGS_TEST := -Wl,--wrap=recv,--wrap=send
|
||||
LIBS_TEST := $(WOLFSSL_LIB) -lpthread -lm
|
||||
|
||||
TEST_BIN := emnet_nonblock_test
|
||||
|
||||
.PHONY: all run clean check-lib
|
||||
|
||||
all: $(TEST_BIN)
|
||||
|
||||
check-lib:
|
||||
@if [ ! -f "$(WOLFSSL_LIB)" ]; then \
|
||||
echo "error: $(WOLFSSL_LIB) not found."; \
|
||||
echo "Build wolfSSL first (see header of this Makefile)."; \
|
||||
exit 1; \
|
||||
fi
|
||||
|
||||
$(TEST_BIN): emnet_nonblock_test.o emnet_shim.o check-lib
|
||||
$(CC) $(LDFLAGS_TEST) -o $@ emnet_nonblock_test.o emnet_shim.o $(LIBS_TEST)
|
||||
|
||||
emnet_nonblock_test.o: emnet_nonblock_test.c
|
||||
$(CC) $(CFLAGS_TEST) -c $< -o $@
|
||||
|
||||
emnet_shim.o: emnet_shim.c IP/IP.h
|
||||
$(CC) $(CFLAGS_TEST) -c $< -o $@
|
||||
|
||||
# Run from the repo root so the relative cert paths resolve.
|
||||
run: $(TEST_BIN)
|
||||
cd $(WOLFSSL_ROOT) && ./tests/emnet/$(TEST_BIN)
|
||||
|
||||
clean:
|
||||
rm -f *.o $(TEST_BIN)
|
||||
@@ -0,0 +1,223 @@
|
||||
/* emnet_nonblock_test.c -- non-blocking TLS 1.3 handshake over a
|
||||
* socketpair, with wolfSSL built for WOLFSSL_EMNET and the recv/send
|
||||
* error surface translated by emnet_shim.c.
|
||||
*
|
||||
* Asserts the steady-state contract of wolfSSL's WOLFSSL_EMNET path:
|
||||
* when the underlying socket would block, wolfSSL_get_error returns
|
||||
* WOLFSSL_ERROR_WANT_READ / WOLFSSL_ERROR_WANT_WRITE and the handshake
|
||||
* completes without spurious fatal errors. Guards against regressions
|
||||
* of a prior bug where the WOLFSSL_EMNET branch in wolfSSL_LastError()
|
||||
* was shadowed by a combined WOLFSSL_LINUXKM||WOLFSSL_EMNET arm that
|
||||
* inverted the sign of IP_ERR_WOULD_BLOCK, causing TranslateIoReturnCode
|
||||
* to surface WOLFSSL_CBIO_ERR_GENERAL on would-block.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <wolfssl/options.h>
|
||||
#include <wolfssl/ssl.h>
|
||||
|
||||
#define MAX_ITERS 200 /* handshake loop safety cap */
|
||||
#define POLL_MS 500
|
||||
|
||||
#define CERT_PATH "certs/server-ecc.pem"
|
||||
#define KEY_PATH "certs/ecc-key.pem"
|
||||
#define CA_PATH "certs/ca-ecc-cert.pem"
|
||||
|
||||
struct side {
|
||||
int fd;
|
||||
const char *name;
|
||||
WOLFSSL *ssl;
|
||||
int (*fn)(WOLFSSL *);
|
||||
int saw_would_block;
|
||||
int completed;
|
||||
int failed;
|
||||
int last_err;
|
||||
};
|
||||
|
||||
static void set_nonblock(int fd)
|
||||
{
|
||||
int flags = fcntl(fd, F_GETFL, 0);
|
||||
if (flags < 0) {
|
||||
perror("fcntl F_GETFL");
|
||||
exit(2);
|
||||
}
|
||||
if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) {
|
||||
perror("fcntl F_SETFL O_NONBLOCK");
|
||||
exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait for the socket to become readable or writable. Returns 0 on
|
||||
* success (ready or timeout, caller retries), -1 on hard failure. */
|
||||
static int wait_io(struct side *s, short events, int iter)
|
||||
{
|
||||
struct pollfd pfd = { .fd = s->fd, .events = events };
|
||||
int r = poll(&pfd, 1, POLL_MS);
|
||||
if (r >= 0)
|
||||
return 0;
|
||||
if (errno == EINTR)
|
||||
return 0;
|
||||
fprintf(stderr, "FAIL: %s: poll failed at iter=%d: %s\n",
|
||||
s->name, iter, strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void *run_side(void *arg)
|
||||
{
|
||||
struct side *s = (struct side *)arg;
|
||||
int iter;
|
||||
|
||||
for (iter = 0; iter < MAX_ITERS; iter++) {
|
||||
int ret = s->fn(s->ssl);
|
||||
if (ret == WOLFSSL_SUCCESS) {
|
||||
s->completed = 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int err = wolfSSL_get_error(s->ssl, ret);
|
||||
s->last_err = err;
|
||||
|
||||
if (err == WOLFSSL_ERROR_WANT_READ) {
|
||||
s->saw_would_block = 1;
|
||||
if (wait_io(s, POLLIN, iter) < 0) {
|
||||
s->failed = 1;
|
||||
return NULL;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (err == WOLFSSL_ERROR_WANT_WRITE) {
|
||||
s->saw_would_block = 1;
|
||||
if (wait_io(s, POLLOUT, iter) < 0) {
|
||||
s->failed = 1;
|
||||
return NULL;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Anything else on a non-blocking handshake is a failure. */
|
||||
fprintf(stderr,
|
||||
"FAIL: %s: wolfSSL_get_error=%d after iter=%d. "
|
||||
"Expected WANT_READ/WANT_WRITE on a non-blocking socketpair. "
|
||||
"Indicates a regression in the WOLFSSL_EMNET error-translation "
|
||||
"path in src/wolfio.c:wolfSSL_LastError.\n",
|
||||
s->name, err, iter);
|
||||
s->failed = 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
fprintf(stderr, "FAIL: %s: handshake did not complete within %d "
|
||||
"iterations (last err=%d)\n",
|
||||
s->name, MAX_ITERS, s->last_err);
|
||||
s->failed = 1;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int sv[2];
|
||||
if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) < 0) {
|
||||
perror("socketpair");
|
||||
return 2;
|
||||
}
|
||||
set_nonblock(sv[0]);
|
||||
set_nonblock(sv[1]);
|
||||
|
||||
wolfSSL_Init();
|
||||
|
||||
WOLFSSL_CTX *sctx = wolfSSL_CTX_new(wolfTLSv1_3_server_method());
|
||||
WOLFSSL_CTX *cctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method());
|
||||
if (!sctx || !cctx) {
|
||||
fprintf(stderr, "wolfSSL_CTX_new failed\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
if (wolfSSL_CTX_use_certificate_file(sctx, CERT_PATH,
|
||||
WOLFSSL_FILETYPE_PEM) != WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "failed to load server cert %s\n", CERT_PATH);
|
||||
return 2;
|
||||
}
|
||||
if (wolfSSL_CTX_use_PrivateKey_file(sctx, KEY_PATH,
|
||||
WOLFSSL_FILETYPE_PEM) != WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "failed to load server key %s\n", KEY_PATH);
|
||||
return 2;
|
||||
}
|
||||
if (wolfSSL_CTX_load_verify_locations(cctx, CA_PATH, NULL)
|
||||
!= WOLFSSL_SUCCESS) {
|
||||
fprintf(stderr, "failed to load CA %s\n", CA_PATH);
|
||||
return 2;
|
||||
}
|
||||
|
||||
WOLFSSL *server_ssl = wolfSSL_new(sctx);
|
||||
WOLFSSL *client_ssl = wolfSSL_new(cctx);
|
||||
if (!server_ssl || !client_ssl) {
|
||||
fprintf(stderr, "wolfSSL_new failed\n");
|
||||
return 2;
|
||||
}
|
||||
|
||||
wolfSSL_set_fd(server_ssl, sv[0]);
|
||||
wolfSSL_set_fd(client_ssl, sv[1]);
|
||||
|
||||
struct side server = { .fd = sv[0], .name = "server",
|
||||
.ssl = server_ssl, .fn = wolfSSL_accept };
|
||||
struct side client = { .fd = sv[1], .name = "client",
|
||||
.ssl = client_ssl, .fn = wolfSSL_connect };
|
||||
|
||||
pthread_t st, ct;
|
||||
int prc;
|
||||
prc = pthread_create(&st, NULL, run_side, &server);
|
||||
if (prc != 0) {
|
||||
fprintf(stderr, "FAIL: pthread_create(server): %s\n", strerror(prc));
|
||||
return 2;
|
||||
}
|
||||
prc = pthread_create(&ct, NULL, run_side, &client);
|
||||
if (prc != 0) {
|
||||
fprintf(stderr, "FAIL: pthread_create(client): %s\n", strerror(prc));
|
||||
pthread_join(st, NULL);
|
||||
return 2;
|
||||
}
|
||||
prc = pthread_join(st, NULL);
|
||||
if (prc != 0) {
|
||||
fprintf(stderr, "FAIL: pthread_join(server): %s\n", strerror(prc));
|
||||
return 2;
|
||||
}
|
||||
prc = pthread_join(ct, NULL);
|
||||
if (prc != 0) {
|
||||
fprintf(stderr, "FAIL: pthread_join(client): %s\n", strerror(prc));
|
||||
return 2;
|
||||
}
|
||||
|
||||
int rc = 0;
|
||||
if (server.failed || client.failed) {
|
||||
rc = 1;
|
||||
} else if (!server.completed || !client.completed) {
|
||||
fprintf(stderr, "FAIL: handshake incomplete (server=%d client=%d)\n",
|
||||
server.completed, client.completed);
|
||||
rc = 1;
|
||||
} else if (!server.saw_would_block && !client.saw_would_block) {
|
||||
fprintf(stderr, "FAIL: handshake completed but never hit a "
|
||||
"non-blocking path. Test scaffolding not exercising "
|
||||
"the WOLFSSL_EMNET error-translation code.\n");
|
||||
rc = 1;
|
||||
} else {
|
||||
printf("OK: handshake completed, would-block paths exercised\n");
|
||||
}
|
||||
|
||||
wolfSSL_free(server_ssl);
|
||||
wolfSSL_free(client_ssl);
|
||||
wolfSSL_CTX_free(sctx);
|
||||
wolfSSL_CTX_free(cctx);
|
||||
wolfSSL_Cleanup();
|
||||
close(sv[0]);
|
||||
close(sv[1]);
|
||||
return rc;
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
/* emnet_shim.c -- POSIX-backed shim for the emNET (embOS/IP) socket ABI
|
||||
* used by wolfSSL when WOLFSSL_EMNET is defined.
|
||||
*
|
||||
* The goal is to reproduce the error-reporting contract of emNET on top
|
||||
* of stock Linux BSD sockets: when the underlying socket signals
|
||||
* would-block, connection reset, etc., the shim surfaces the
|
||||
* corresponding IP_ERR_* negative constant (emNET convention) instead
|
||||
* of -1/errno (POSIX convention). This is exactly what wolfSSL's
|
||||
* WOLFSSL_EMNET branch in wolfio.h/wolfio.c was written to consume, so
|
||||
* CI can drive the non-blocking handshake paths without the real
|
||||
* SEGGER stack.
|
||||
*
|
||||
* Linker wrapping:
|
||||
* -Wl,--wrap=recv,--wrap=send
|
||||
* hooks wolfSSL's RECV_FUNCTION/SEND_FUNCTION (which are the
|
||||
* unqualified POSIX send/recv on the WOLFSSL_EMNET build) without
|
||||
* patching any wolfSSL source.
|
||||
*/
|
||||
|
||||
#include "IP/IP.h"
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <errno.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
|
||||
/* Forward declarations for the linker's --wrap mechanism. */
|
||||
ssize_t __real_recv(int sd, void *buf, size_t len, int flags);
|
||||
ssize_t __real_send(int sd, const void *buf, size_t len, int flags);
|
||||
|
||||
/* Translate a POSIX errno value into the emNET IP_ERR_* space. */
|
||||
static int emnet_errno_to_ip_err(int err)
|
||||
{
|
||||
/* Linux, where this shim runs in CI, defines EWOULDBLOCK == EAGAIN,
|
||||
* so EAGAIN covers both. */
|
||||
switch (err) {
|
||||
case EAGAIN:
|
||||
return IP_ERR_WOULD_BLOCK;
|
||||
case ECONNRESET:
|
||||
return IP_ERR_CONN_RESET;
|
||||
case ECONNREFUSED:
|
||||
return IP_ERR_CONN_REFUSED;
|
||||
case ECONNABORTED:
|
||||
return IP_ERR_CONN_ABORTED;
|
||||
case EPIPE:
|
||||
return IP_ERR_PIPE;
|
||||
default:
|
||||
return IP_ERR_FAULT;
|
||||
}
|
||||
}
|
||||
|
||||
/* recv wrapper: preserve success/close semantics; on error return the
|
||||
* emNET-style negative error code in place of -1/errno. wolfSSL's
|
||||
* TranslateIoReturnCode uses err < 0 to branch into error handling and
|
||||
* then compares against SOCKET_EWOULDBLOCK == IP_ERR_WOULD_BLOCK. */
|
||||
ssize_t __wrap_recv(int sd, void *buf, size_t len, int flags)
|
||||
{
|
||||
ssize_t ret = __real_recv(sd, buf, len, flags);
|
||||
if (ret < 0) {
|
||||
return (ssize_t)emnet_errno_to_ip_err(errno);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
ssize_t __wrap_send(int sd, const void *buf, size_t len, int flags)
|
||||
{
|
||||
ssize_t ret = __real_send(sd, buf, len, flags);
|
||||
if (ret < 0) {
|
||||
return (ssize_t)emnet_errno_to_ip_err(errno);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* IP_SOCK_getsockopt: kept to satisfy the emNET ABI surface expected
|
||||
* by WOLFSSL_EMNET-linked code. Delegates to POSIX getsockopt and, for
|
||||
* SO_ERROR, maps the returned POSIX errno value into emNET's IP_ERR_*
|
||||
* space so callers see emNET-style error reporting. */
|
||||
int IP_SOCK_getsockopt(int sd, int level, int optname,
|
||||
void *optval, int *optlen)
|
||||
{
|
||||
socklen_t posix_len;
|
||||
int rc;
|
||||
|
||||
if (optlen == NULL) {
|
||||
errno = EINVAL;
|
||||
return -1;
|
||||
}
|
||||
posix_len = (socklen_t)*optlen;
|
||||
rc = getsockopt(sd, level, optname, optval, &posix_len);
|
||||
*optlen = (int)posix_len;
|
||||
|
||||
if (rc == 0 && level == SOL_SOCKET && optname == SO_ERROR
|
||||
&& optval != NULL && posix_len >= (socklen_t)sizeof(int)) {
|
||||
int so_err;
|
||||
memcpy(&so_err, optval, sizeof(so_err));
|
||||
if (so_err != 0) {
|
||||
so_err = emnet_errno_to_ip_err(so_err);
|
||||
memcpy(optval, &so_err, sizeof(so_err));
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
+5
-1
@@ -81,5 +81,9 @@ EXTRA_DIST += tests/unit.h \
|
||||
tests/NCONF_test.cnf \
|
||||
tests/test-tls-downgrade.conf \
|
||||
tests/TXT_DB.txt \
|
||||
tests/utils.h
|
||||
tests/utils.h \
|
||||
tests/emnet/IP/IP.h \
|
||||
tests/emnet/emnet_shim.c \
|
||||
tests/emnet/emnet_nonblock_test.c \
|
||||
tests/emnet/Makefile
|
||||
DISTCLEANFILES+= tests/.libs/unit.test
|
||||
|
||||
Reference in New Issue
Block a user