After a bunch of study, I figured it out!
(If you are stumbling upon this question, first ensure you have #include <arpa/inet.h>
at the top of your file, as already stated in the question.)
It turns out that putting -std=c17
is more-restrictive than just leaving that part off. WithOUT -std=c17
, the gcc compiler includes many more features which are not part of the C standard, including POSIX features, gcc extensions, and special Linux system features. So, using -std=c17
causes the declaration of inet_aton()
to get excluded from <arpa/inet.h>
, even though it would otherwise be included.
Here are the various fixes, incl. using -std=gnu17
vs -std=c17
:
- [Not recommended, since this is a hack, not a fix] Remove
-Werror
from the build command so that it compiles with that error as a warning instead of as an error.
- Remove
-std=c17
so that you get the extra features provided by gcc instead of being limited to the C standard.
- [GREAT OPTION] Use the less-restrictive
-std=gnu17
instead of -std=c17
(thanks, @ikegami!)
- [ANOTHER GREAT OPTION] Add
-D_DEFAULT_SOURCE
to your build command to define the macro _DEFAULT_SOURCE
for your entire program, so that gcc will allow in inet_aton()
from <arpa/inet.h>
for you (more on this below). Here is my full build command now:
gcc -Wall -Wextra -Werror -O3 -std=c17 -D_DEFAULT_SOURCE \
socket__geeksforgeeks_udp_server_GS_edit_GREAT.c -o bin/server \
-lm && bin/server
- [Alternative to the option just above] Add
#define _DEFAULT_SOURCE
to the top of your main source file before including any headers, so that it is defined before your headers get included, thereby allowing in inet_aton()
from where you include <arpa/inet.h>
.
- [I plan on doing this regardless of which option above I also use] Use the better POSIX
inet_pton()
"internet presentation (textual) to network (binary) format" function (see here and here) instead of the non-POSIX inet_aton()
"internet ascii string to network" function (see here). See more on this below.
Any of those options work. I recommend option 3, 4, or 5, but I prefer 3 the most, and 4 after that. In all cases, I plan on also implementing option 6, however.
"Feature test macros" in gcc
How do options 4 and 5 above work? I learned they are part of gcc's "feature test macros" system. Read all about it here: https://man7.org/linux/man-pages/man7/feature_test_macros.7.html. In essence, certain features of gnu's glibc library implementation are excluded as you include header files unless certain "feature test macros" are defined before you include that header.
Go look at the man
page for inet_aton()
: https://man7.org/linux/man-pages/man3/inet.3.html. It states at the bottom of the "SYNOPSIS" section at the top of the page:
Feature Test Macro Requirements for glibc (see feature_test_macros(7)):
inet_aton()
, inet_ntoa()
:
- Since glibc 2.19:
- In glibc up to and including 2.19:
_BSD_SOURCE || _BSD_SOURCE
So, check your version of glibc with ldd --version
. Mine shows 2.27
:
$ ldd --version
ldd (Ubuntu GLIBC 2.27-3ubuntu1.5) 2.27
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Written by Roland McGrath and Ulrich Drepper.
That means the part that says Since glibc 2.19:
above applies to me, and I must define the "feature test macro" _DEFAULT_SOURCE
in the source code before including any headers (my option 5 above), or in the build command (my option 4 above) to include these features, including inet_aton()
.
Simply leaving off -std=c17
also works because the feature_test_macros man page (also available at the command-line via man feature_test_macros
) states:
Note that, as described below, some feature test macros are defined by default, so that it may not always be necessary to explicitly specify the feature test macro(s) shown in the SYNOPSIS.
and:
_DEFAULT_SOURCE
(since glibc 2.19)
This macro can be defined to ensure that the "default"
definitions are provided even when the defaults would
otherwise be disabled, as happens when individual macros
are explicitly defined, or the compiler is invoked in one
of its "standard" modes (e.g., cc -std=c99
). Defining
_DEFAULT_SOURCE
without defining other individual macros
or invoking the compiler in one of its "standard" modes
has no effect.
The "default" definitions comprise those required by
POSIX.1-2008 and ISO C99, as well as various definitions
originally derived from BSD and System V. On glibc 2.19
and earlier, these defaults were approximately equivalent
to explicitly defining the following:
cc -D_BSD_SOURCE -D_SVID_SOURCE
-D_POSIX_C_SOURCE=200809
Notice that it mentions -std=c99
. That concept applies to my usage of -std=c17
, so I have to define _DEFAULT_SOURCE
to counter the feature-removal effect created by -std=c17
.
Just use the POSIX-compliant inet_pton()
instead of the non-POSIX inet_aton()
Lastly, an even better option than using the non-POSIX function inet_aton()
("ASCII string to network" function) is to use the POSIX function inet_pton()
("presentation string to network" function).
This source (https://man7.org/linux/man-pages/man3/inet.3.html) says (emphasis added):
inet_aton()
is not specified in POSIX.1, but is available on most systems.
inet_pton()
is even better, and available with my original build command in the question, and is described here: https://man7.org/linux/man-pages/man3/inet_pton.3.html. It does not require any gcc feature test macros to include it, and it has more functionality than inet_aton
and handles both IPv4 address families (AF_INET
) and IPv6 address families (AF_INET6
).
See also the gcc glibc one-page user manual. Here is the link to the int inet_pton (int af, const char *cp, void *buf)
part: https://www.gnu.org/software/libc/manual/html_mono/libc.html#index-inet_005fpton (emphasis added):
This function converts an Internet address (either IPv4 or IPv6) from presentation (textual) to network (binary) format. af
should be either AF_INET
or AF_INET6
, as appropriate for the type of address being converted. cp
is a pointer to the input string, and buf
is a pointer to a buffer for the result. It is the caller’s responsibility to make sure the buffer is large enough.
Full example of inet_aton()
and inet_pton()
For my full example code, including error checking, search this file for inet_aton
and inet_pton
(recommended), in this file from my eRCaGuy_hello_world repo here: socket__geeksforgeeks_udp_server_GS_edit_GREAT.c.
Example: here is my inet_pton()
example code:
int retcode;
// ...
retcode = inet_pton(AF_INET, "127.0.0.1", &addr_server.sin_addr); // <--- Option 3/3 to set the IP address
if (retcode != 1)
{
printf("`inet_pton()` failed! ");
if (retcode == 0)
{
printf("The source IP address string does not contain a character string representing "
"a valid network address in the specified address family.\n");
}
else if (retcode == -1)
{
printf("Invalid address family (AF) parameter was passed-in as the 1st argument. "
"errno = %i: %s\n", errno, strerror(errno));
}
}