├── .github ├── CONTRIBUTING.md └── workflows │ └── build.yml ├── .gitignore ├── LICENSE ├── Makefile.am ├── README ├── autogen.sh ├── configure.ac ├── example.c ├── icmp.h ├── iping.c └── libicmp.c /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing to libicmp 2 | ======================= 3 | 4 | Thank you for considering contributing back to [the community][1]! 5 | 6 | There are a few things we would like you to consider when filing an 7 | issue or making a pull request with this project: 8 | 9 | 1. If you are filing a bug report or feature request 10 | 11 | Please take the time to check if an issue already has been filed 12 | matching your problem 13 | 14 | 2. What version are you running, have you tried the latest release? 15 | 16 | UNIX distributions often package and test software for their 17 | particular brand. If you are using a pre-packaged version, 18 | then please file a bug with that distribution instead. 19 | 20 | 3. Coding Style 21 | 22 | Lines are allowed to be longer than 72 characters these days, there 23 | is no enforced maximum line length. 24 | 25 | > **Tip:** Always submit code that follows the style of surrounding code! 26 | 27 | The coding style itself is strictly Linux [KNF][], like GIT it is 28 | becoming a de facto standard for C programming 29 | 30 | https://www.kernel.org/doc/Documentation/CodingStyle 31 | 32 | 4. Logical Change Sets 33 | 34 | Changes should be broken down into logical units that add a feature 35 | or fix a bug. Keep changes separate from each other and do not mix a 36 | bug fix with a whitespace cleanup or a new feature addition. 37 | 38 | This is important not only for readilibity, or for the possibility of 39 | maintainers to revert or isolate changes when bisecting for a bug. 40 | It also increases the chances of having your change accepted. 41 | 42 | 5. Commit messages 43 | 44 | Commit messages exist to track *why* a change was made. Try to be as 45 | clear and concise as possible in your commit messages, and always, be 46 | proud of your work and set up a proper GIT identity for your commits: 47 | 48 | git config --global user.name "J. Random Hacker" 49 | git config --global user.email random.j.hacker@example.com 50 | 51 | See this helpful guide for how to write simple, readable commit 52 | messages, or have at least a look at the below example. 53 | 54 | http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html 55 | 56 | 57 | Example 58 | ------- 59 | 60 | Example commit message from the [Pro Git][gitbook] online book, notice 61 | how `git commit -s` is used to automatically add a `Signed-off-by`: 62 | 63 | Capitalized, short (50 chars or less) summary 64 | 65 | More detailed explanatory text, if necessary. Wrap it to about 72 66 | characters or so. In some contexts, the first line is treated as the 67 | subject of an email and the rest of the text as the body. The blank 68 | line separating the summary from the body is critical (unless you omit 69 | the body entirely); tools like rebase can get confused if you run the 70 | two together. 71 | 72 | Write your commit message in the imperative: "Fix bug" and not "Fixed bug" 73 | or "Fixes bug." This convention matches up with commit messages generated 74 | by commands like git merge and git revert. 75 | 76 | Further paragraphs come after blank lines. 77 | 78 | - Bullet points are okay, too 79 | 80 | - Typically a hyphen or asterisk is used for the bullet, followed by a 81 | single space, with blank lines in between, but conventions vary here 82 | 83 | - Use a hanging indent 84 | 85 | Signed-off-by: J. Random Hacker 86 | 87 | 88 | [1]: https://opensource.com/resources/what-open-source 89 | [KNF]: https://en.wikipedia.org/wiki/Kernel_Normal_Form 90 | [gitbook]: https://git-scm.com/book/ch5-2.html 91 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Bob the Builder 2 | 3 | # Run on all branches, including all pull requests, except the 'dev' 4 | # branch since that's where we run Coverity Scan (limited tokens/day) 5 | on: 6 | push: 7 | branches: 8 | - '**' 9 | - '!dev' 10 | pull_request: 11 | branches: 12 | - '**' 13 | 14 | jobs: 15 | build: 16 | # Verify we can build on latest Ubuntu with both gcc and clang 17 | name: ${{ matrix.compiler }} 18 | runs-on: ubuntu-latest 19 | strategy: 20 | matrix: 21 | compiler: [gcc, clang] 22 | fail-fast: false 23 | env: 24 | MAKEFLAGS: -j3 25 | CC: ${{ matrix.compiler }} 26 | LD_LIBRARY_PATH: /tmp/foo/lib 27 | steps: 28 | - name: Install dependencies 29 | run: | 30 | sudo apt-get -y update 31 | sudo apt-get -y install tree 32 | - uses: actions/checkout@v2 33 | - name: Configure 34 | run: | 35 | ./autogen.sh 36 | ./configure --prefix= 37 | - name: Build 38 | run: | 39 | make V=1 40 | - name: Install 41 | run: | 42 | DESTDIR=/tmp/foo make install-strip 43 | tree /tmp/foo 44 | ldd /tmp/foo/sbin/iping 45 | size /tmp/foo/sbin/iping 46 | /tmp/foo/sbin/iping -h 47 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .deps 2 | .libs 3 | *~ 4 | *.a 5 | *.o 6 | *.lo 7 | *.la 8 | *.log 9 | Makefile 10 | Makefile.in 11 | aclocal.m4 12 | ar-lib 13 | autom4te.cache 14 | compile 15 | config.h 16 | config.h.in 17 | config.sub 18 | config.guess 19 | config.status 20 | configure 21 | depcomp 22 | example 23 | install-sh 24 | iping 25 | libtool 26 | ltmain.sh 27 | missing 28 | stamp-h1 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2000 Tim Lawless 2 | Copyright (c) 2011-2022 Joachim Wiberg 3 | 4 | Permission to use, copy, modify, and/or distribute this software for any 5 | purpose with or without fee is hereby granted, provided that the above 6 | copyright notice and this permission notice appear in all copies. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | lib_LTLIBRARIES = libicmp.la 2 | pkgincludedir = $(includedir)/icmp 3 | pkginclude_HEADERS = icmp.h 4 | 5 | check_PROGRAMS = example 6 | example_SOURCES = example.c 7 | example_CPPFLAGS = -W -Wall -Wextra 8 | example_LDADD = libicmp.la 9 | 10 | sbin_PROGRAMS = iping 11 | iping_SOURCES = iping.c 12 | iping_CFLAGS = -W -Wall -Wextra 13 | iping_LDADD = libicmp.la 14 | 15 | libicmp_la_SOURCES = libicmp.c icmp.h 16 | libicmp_la_CFLAGS = -W -Wall -Wextra 17 | libicmp_la_LDFLAGS = $(AM_LDFLAGS) -version-info 0:0:0 18 | 19 | doc_DATA = README LICENSE 20 | EXTRA_DIST = README LICENSE 21 | 22 | ## Generate MD5 checksum file 23 | MD5 = md5sum 24 | md5-dist: 25 | @for file in $(DIST_ARCHIVES); do \ 26 | $(MD5) $$file > $$file.md5; \ 27 | done 28 | 29 | ## Check if tagged in git 30 | release-hook: 31 | if [ ! `git tag | grep v$(PACKAGE_VERSION)` ]; then \ 32 | echo; \ 33 | printf "\e[1m\e[41mCannot find release tag v$(PACKAGE_VERSION)\e[0m\n"; \ 34 | printf "\e[1m\e[5mDo release anyway?\e[0m "; read yorn; \ 35 | if [ "$$yorn" != "y" -a "$$yorn" != "Y" ]; then \ 36 | printf "OK, aborting release.\n"; \ 37 | exit 1; \ 38 | fi; \ 39 | echo; \ 40 | else \ 41 | echo; \ 42 | printf "\e[1m\e[42mFound GIT release tag v$(PACKAGE_VERSION)\e[0m\n"; \ 43 | printf "\e[1m\e[44m>>Remember to push tags!\e[0m\n"; \ 44 | echo; \ 45 | fi 46 | 47 | ## Target to run when building a release 48 | release: dist release-hook md5-dist 49 | @for file in $(DIST_ARCHIVES); do \ 50 | printf "$$file \tDistribution tarball\n"; \ 51 | printf "$$file.md5\t"; cat $$file.md5 | cut -f1 -d' '; \ 52 | mv $$file* ../; \ 53 | done 54 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | README 2 | 3 | LibICMP is a small and simple API for sending and receiving ICMP packets 4 | from your application. Currently only IPv4 is supported, so any patches 5 | for adding support for IPv6 are most welcome! 6 | 7 | For some useful examples, see the included iping.c and example.c, the 8 | former makes use of more features and the latter is more bare bones. 9 | 10 | LibICMP was initally developed by Tim Lawless and later refactored and 11 | extended upon by Joachim Wiberg. 12 | 13 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | autoreconf -W portability -visfm 4 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_INIT(libICMP, 1.0, https://github.com/troglobit/libicmp/issues, libicmp, https://github.com/troglobit/libicmp) 2 | AM_INIT_AUTOMAKE([1.11 foreign no-dist-gzip dist-xz]) 3 | AM_SILENT_RULES([yes]) 4 | 5 | AC_CONFIG_SRCDIR(libicmp.c) 6 | AM_CONFIG_HEADER(config.h) 7 | AC_CONFIG_FILES([Makefile]) 8 | 9 | AC_PROG_CC 10 | AM_PROG_AR 11 | LT_INIT 12 | 13 | AC_OUTPUT 14 | -------------------------------------------------------------------------------- /example.c: -------------------------------------------------------------------------------- 1 | /* Public Domain example of how to use libICMP */ 2 | #include "icmp.h" 3 | 4 | int main(int argc, char *argv[]) 5 | { 6 | char *host = "localhost"; 7 | struct libicmp *obj; 8 | 9 | if (argc >= 2) 10 | host = argv[1]; 11 | 12 | if (!(obj = icmp_open(host, 0x1337, 0))) 13 | return 1; 14 | 15 | return icmp_ping(obj, 0, 0) == -1; 16 | } 17 | -------------------------------------------------------------------------------- /icmp.h: -------------------------------------------------------------------------------- 1 | /* LibICMP - A simple API for sending and receiving ICMP datagrams. 2 | * 3 | * Copyright (c) 2000 Tim Lawless 4 | * Copyright (c) 2011-2022 Joachim Wiberg 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #ifndef ICMP_ICMP_H_ 20 | #define ICMP_ICMP_H_ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | struct libicmp { 28 | int sd; /* File Descriptor for the ICMP Socket */ 29 | int gai_code; /* Error code from getaddrinfo() */ 30 | uint8_t ttl; /* IP unicast/multicast TTL */ 31 | uint16_t id; /* The ICMP_ECHO ID */ 32 | uint16_t seqno; /* Sequence # */ 33 | char *host; /* Original hostname to ping. */ 34 | struct timeval tv; /* Packet send time */ 35 | int triptime; /* Round trip time */ 36 | }; 37 | 38 | struct libicmp *icmp_open (char *host, uint16_t id, uint8_t ttl); 39 | int icmp_bind (struct libicmp *obj, char *addr); 40 | int icmp_send (struct libicmp *obj, uint8_t type, char *payload, size_t len); 41 | int icmp_recv (struct libicmp *obj, uint8_t type, int timeout, char *payload, size_t len); 42 | int icmp_ping (struct libicmp *obj, char *payload, size_t len); 43 | int icmp_close (struct libicmp *obj); 44 | 45 | int icmp_resolve(struct libicmp *obj, struct addrinfo **ai); 46 | char *icmp_ntoa (struct libicmp *obj, char *buf, size_t len); 47 | 48 | int icmp_err (struct libicmp *obj); 49 | const char *icmp_errstr (struct libicmp *obj); 50 | 51 | #endif /* ICMP_ICMP_H_ */ 52 | 53 | /** 54 | * Local Variables: 55 | * indent-tabs-mode: t 56 | * c-file-style: "linux" 57 | * End: 58 | */ 59 | -------------------------------------------------------------------------------- /iping.c: -------------------------------------------------------------------------------- 1 | /* iping - a very small and free ping utility to showcase libICMP 2 | * 3 | * Copyright (c) 2011-2022 Joachim Wiberg 4 | * 5 | * Permission to use, copy, modify, and/or distribute this software for any 6 | * purpose with or without fee is hereby granted, provided that the above 7 | * copyright notice and this permission notice appear in all copies. 8 | * 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | */ 17 | 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #include "icmp.h" 29 | #define PATTERN "!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~ " 30 | 31 | char *ident = PACKAGE_NAME; 32 | 33 | static int usage(int code) 34 | { 35 | printf("Usage: %s [OPTIONS] HOST\n" 36 | "\n" 37 | "Options:\n" 38 | " -h This help text\n" 39 | " -s SIZE Generate payload of SIZE bytes\n" 40 | " -S SOURCE Source IP to use in ICMP datagram, from interface\n" 41 | " -t TTL Set IP time to live (hops)\n" 42 | " -V Verbose operation, dump payload, etc.\n" 43 | " -v Show version information\n" 44 | "\n" 45 | "Bug report address: %-40s\n" 46 | "Project homepage: %s\n\n", ident, PACKAGE_BUGREPORT, PACKAGE_URL); 47 | 48 | return code; 49 | } 50 | 51 | static char *progname(char *arg0) 52 | { 53 | char *nm; 54 | 55 | nm = strrchr(arg0, '/'); 56 | if (nm) 57 | nm++; 58 | else 59 | nm = arg0; 60 | 61 | return nm; 62 | } 63 | 64 | static char *generator(char *buf, size_t buflen) 65 | { 66 | size_t num, len; 67 | static size_t pos = 0; 68 | const char pattern[] = PATTERN; 69 | 70 | len = 72; 71 | if (len > buflen) 72 | len = buflen; 73 | if (pos + len > sizeof(pattern)) { 74 | num = sizeof(pattern) - pos; 75 | len -= num; 76 | } else { 77 | num = 72; 78 | if (num > buflen) 79 | num = buflen; 80 | len = 0; 81 | } 82 | 83 | strncpy(&buf[0], &pattern[pos], num--); 84 | if (len++) 85 | strncpy(&buf[num], pattern, len); 86 | 87 | if (++pos >= sizeof(pattern) - 1) 88 | pos = 0; 89 | 90 | return buf; 91 | } 92 | 93 | int main(int argc, char *argv[]) 94 | { 95 | int len, result = 0, verbose = 0; 96 | char *source_ip = NULL, ch; 97 | char *host, addr[INET_ADDRSTRLEN]; 98 | size_t payloadsz = 0; 99 | uint8_t ttl = 0; 100 | struct libicmp *obj; 101 | 102 | ident = progname(argv[0]); 103 | while ((ch = getopt(argc, argv, "hs:S:t:vV")) != EOF) { 104 | switch (ch) { 105 | case 's': 106 | payloadsz = (size_t)atoi(optarg); 107 | break; 108 | 109 | case 'S': 110 | source_ip = optarg; 111 | break; 112 | 113 | case 't': 114 | ttl = atoi(optarg); 115 | break; 116 | 117 | case 'v': 118 | printf("v%s\n", PACKAGE_VERSION); 119 | return 0; 120 | 121 | case 'V': 122 | verbose = 1; 123 | break; 124 | 125 | case '?': 126 | case 'h': 127 | return usage(0); 128 | 129 | default: 130 | return usage(1); 131 | } 132 | } 133 | 134 | if (optind == argc) 135 | return usage(1); 136 | 137 | host = argv[optind]; 138 | obj = icmp_open(host, 0x1337, ttl); 139 | if (!obj) 140 | err(1, "Failed opening ICMP socket"); 141 | 142 | if (!icmp_ntoa(obj, addr, sizeof(addr))) { 143 | warnx("%s: %s", host, icmp_errstr(obj) ?: ""); 144 | goto exit; 145 | } 146 | 147 | if (source_ip && icmp_bind(obj, source_ip)) { 148 | warnx("%s: %s", source_ip, icmp_errstr(obj) ?: ""); 149 | goto exit; 150 | } 151 | 152 | printf("PING %s (%s)\n", host, addr); 153 | while (1) { 154 | char buf[BUFSIZ]; 155 | size_t buflen = 0; 156 | 157 | if (payloadsz > 0) { 158 | buflen = sizeof(buf) < payloadsz ? sizeof(buf) : payloadsz; 159 | generator(buf, buflen); 160 | } 161 | 162 | if (icmp_send(obj, ICMP_ECHO, buf, buflen)) 163 | goto error; 164 | 165 | len = icmp_recv(obj, ICMP_ECHOREPLY, 5000, buf, sizeof(buf)); 166 | if (len < 0) { 167 | error: 168 | warnx("%s: %s", host, icmp_errstr(obj) ?: ""); 169 | result = 1; 170 | goto exit; 171 | } 172 | 173 | printf("PING reply from %s (%s): icmp_req=%d ttl=%d time=%d.%d ms\n", 174 | host, addr, obj->seqno, obj->ttl, obj->triptime / 10, obj->triptime % 10); 175 | if (verbose && len > 0) { 176 | buf[len] = 0; 177 | printf("\tPayload: %s\n", buf); 178 | } 179 | sleep(1); 180 | } 181 | 182 | exit: 183 | icmp_close(obj); 184 | 185 | return result; 186 | } 187 | 188 | /** 189 | * Local Variables: 190 | * indent-tabs-mode: t 191 | * c-file-style: "linux" 192 | * End: 193 | */ 194 | -------------------------------------------------------------------------------- /libicmp.c: -------------------------------------------------------------------------------- 1 | /* LibICMP - A simple API for sending and receiving ICMP datagrams. 2 | * 3 | * Copyright (c) 2000 Tim Lawless 4 | * Copyright (c) 2011-2022 Joachim Wiberg 5 | * 6 | * Permission to use, copy, modify, and/or distribute this software for any 7 | * purpose with or without fee is hereby granted, provided that the above 8 | * copyright notice and this permission notice appear in all copies. 9 | * 10 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 15 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 16 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | #include "icmp.h" 31 | 32 | static uint16_t in_cksum(uint16_t *buf, int nwords) 33 | { 34 | uint32_t sum; 35 | 36 | for (sum = 0; nwords > 0; nwords--) 37 | sum += *buf++; 38 | 39 | sum = (sum >> 16) + (sum & 0xffff); 40 | sum += (sum >> 16); 41 | 42 | return ~sum; 43 | } 44 | 45 | 46 | /* 47 | * If you are opening up a socket for listening, set both paramaters to 0 48 | */ 49 | struct libicmp *icmp_open(char *host, uint16_t id, uint8_t ttl) 50 | { 51 | struct libicmp *obj; 52 | socklen_t ttl_len; 53 | 54 | obj = calloc(1, sizeof(struct libicmp)); 55 | if (!obj) 56 | return NULL; 57 | 58 | obj->sd = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); 59 | obj->id = id; 60 | obj->host = strdup(host); 61 | 62 | if (obj->sd < 0) { 63 | free(obj->host); 64 | free(obj); 65 | 66 | return NULL; 67 | } 68 | 69 | ttl_len = sizeof(obj->ttl); 70 | if (ttl) { 71 | setsockopt(obj->sd, IPPROTO_IP, IP_TTL, &ttl, sizeof(ttl)); 72 | setsockopt(obj->sd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl)); 73 | getsockopt(obj->sd, IPPROTO_IP, IP_TTL, &obj->ttl, &ttl_len); 74 | } 75 | getsockopt(obj->sd, IPPROTO_IP, IP_TTL, &obj->ttl, &ttl_len); 76 | 77 | return obj; 78 | } 79 | 80 | static int do_resolve(char *addr, struct addrinfo **ai) 81 | { 82 | int code; 83 | struct addrinfo hints; 84 | 85 | res_init(); /* Reinitialize resolver every time to prevent cache misses due to 86 | * NAME-->IP DNS changes, or outages in round robin DNS setups. */ 87 | 88 | memset(&hints, 0, sizeof(struct addrinfo)); 89 | hints.ai_flags = 0; 90 | hints.ai_protocol = 0; /* Any protocol */ 91 | 92 | code = getaddrinfo(addr, NULL, &hints, ai); 93 | if (code) { 94 | errno = EADDRNOTAVAIL; 95 | return code; 96 | } 97 | 98 | return 0; 99 | } 100 | 101 | int icmp_bind(struct libicmp *obj, char *addr) 102 | { 103 | int result; 104 | struct addrinfo *ai; 105 | 106 | if (!obj || !addr) { 107 | errno = EINVAL; 108 | return -1; 109 | } 110 | 111 | obj->gai_code = do_resolve(addr, &ai); 112 | if (obj->gai_code) 113 | return -1; 114 | 115 | result = bind(obj->sd, ai->ai_addr, ai->ai_addrlen); 116 | freeaddrinfo(ai); 117 | 118 | return result; 119 | } 120 | 121 | int icmp_resolve(struct libicmp *obj, struct addrinfo **ai) 122 | { 123 | if (!obj || !ai) { 124 | errno = EINVAL; 125 | return -1; 126 | } 127 | 128 | return obj->gai_code = do_resolve(obj->host, ai); 129 | } 130 | 131 | char *icmp_ntoa(struct libicmp *obj, char *buf, size_t len) 132 | { 133 | char *result = buf; 134 | struct addrinfo *ai; 135 | 136 | if (!buf || len < INET_ADDRSTRLEN) { 137 | errno = EINVAL; 138 | return NULL; 139 | } 140 | 141 | obj->gai_code = icmp_resolve(obj, &ai); 142 | if (obj->gai_code) 143 | return NULL; 144 | 145 | /* NI_NUMERICHOST avoids DNS lookup. */ 146 | if (getnameinfo(ai->ai_addr, ai->ai_addrlen, buf, len, NULL, 0, NI_NUMERICHOST)) 147 | result = NULL; 148 | freeaddrinfo(ai); 149 | 150 | return result; 151 | } 152 | 153 | int icmp_err(struct libicmp *obj) 154 | { 155 | if (!obj) { 156 | errno = EINVAL; 157 | return -1; 158 | } 159 | 160 | return obj->gai_code; 161 | } 162 | 163 | const char *icmp_errstr(struct libicmp *obj) 164 | { 165 | if (!obj) { 166 | errno = EINVAL; 167 | return NULL; 168 | } 169 | 170 | if (obj->gai_code) 171 | return gai_strerror(obj->gai_code); 172 | 173 | return strerror(errno); 174 | } 175 | 176 | int icmp_send(struct libicmp *obj, uint8_t type, char *payload, size_t len) 177 | { 178 | int result; 179 | char buffer[BUFSIZ]; 180 | struct timeval now; 181 | struct icmphdr *icmp; 182 | struct addrinfo *ai; 183 | 184 | if (!obj) { 185 | errno = EINVAL; 186 | return -1; 187 | } 188 | 189 | if (sizeof(buffer) < len + sizeof(now)) { 190 | errno = EMSGSIZE; 191 | return -1; 192 | } 193 | 194 | obj->gai_code = icmp_resolve(obj, &ai); 195 | if (obj->gai_code) 196 | return -1; 197 | 198 | memset(buffer, 0, sizeof(buffer)); 199 | gettimeofday(&now, NULL); 200 | 201 | /* ICMP Payload is our current time + any user defined payload */ 202 | memcpy((buffer + sizeof(struct icmphdr)), &now, sizeof(now)); 203 | if (payload && len) 204 | memcpy((buffer + sizeof(struct icmphdr) + sizeof(now)), payload, len); 205 | 206 | icmp = (struct icmphdr *)buffer; 207 | icmp->type = type; 208 | icmp->un.echo.id = htons(obj->id); 209 | icmp->un.echo.sequence = htons(obj->seqno); 210 | icmp->checksum = in_cksum((u_short *)icmp, sizeof(struct icmphdr) + sizeof(now) + len); 211 | 212 | result = sendto(obj->sd, buffer, sizeof(struct icmphdr) + sizeof(now) + len, 0, 213 | ai->ai_addr, sizeof(struct sockaddr)); 214 | freeaddrinfo(ai); 215 | if (result < 0) 216 | return -1; 217 | 218 | return 0; 219 | } 220 | 221 | 222 | int icmp_recv(struct libicmp *obj, uint8_t type, int timeout, char *payload, size_t len) 223 | { 224 | int i, checksum; 225 | char *ptr, buf[BUFSIZ]; 226 | struct iphdr *ip; 227 | struct icmphdr *icmp; 228 | 229 | if (!obj) { 230 | errno = EINVAL; 231 | return -1; 232 | } 233 | 234 | ip = (struct iphdr *)buf; 235 | icmp = (struct icmphdr *)(buf + sizeof(struct iphdr)); 236 | ptr = buf + sizeof(struct iphdr) + sizeof(struct icmphdr); 237 | 238 | while (1) { 239 | int result = 0; 240 | struct pollfd pfd = { obj->sd, POLLIN | POLLPRI, 0 }; 241 | 242 | result = poll(&pfd, 1, timeout); 243 | if (result <= 0) { 244 | if (result == 0) 245 | errno = ETIMEDOUT; 246 | 247 | return 0; 248 | } 249 | 250 | if (pfd.revents & (POLLIN | POLLPRI)) { 251 | size_t datalen; 252 | struct timeval now; 253 | 254 | i = read(obj->sd, buf, BUFSIZ); 255 | if (i < 0) 256 | return -1; 257 | 258 | gettimeofday(&now, NULL); 259 | 260 | if (ip->protocol != 1 || icmp->type != type || 261 | icmp->un.echo.id != htons(obj->id) || icmp->un.echo.sequence != htons(obj->seqno)) 262 | continue; 263 | obj->seqno++; 264 | 265 | checksum = icmp->checksum; 266 | icmp->checksum = 0; 267 | datalen = i - sizeof(struct iphdr); 268 | if (checksum != in_cksum((u_short *)icmp, datalen)) { 269 | errno = EIO; 270 | return -1; 271 | } 272 | datalen -= sizeof(struct icmphdr); 273 | 274 | memcpy(&obj->tv, ptr, sizeof(obj->tv)); 275 | timersub(&now, &obj->tv, &now); 276 | 277 | /* precision: tenths of milliseconds */ 278 | obj->triptime = now.tv_sec * 10000 + (now.tv_usec / 100); 279 | 280 | datalen -= sizeof(obj->tv); 281 | if (len < datalen) 282 | datalen = len; 283 | memcpy(payload, ptr + sizeof(obj->tv), datalen); 284 | 285 | return datalen; 286 | } 287 | } 288 | } 289 | 290 | 291 | int icmp_ping(struct libicmp *obj, char *payload, size_t paylen) 292 | { 293 | int len; 294 | char buf[BUFSIZ]; 295 | 296 | if (icmp_send(obj, ICMP_ECHO, payload, paylen)) 297 | return -1; 298 | 299 | len = icmp_recv(obj, ICMP_ECHOREPLY, 5000, buf, sizeof(buf)); 300 | if (len < 0) 301 | return -1; 302 | 303 | return len; 304 | } 305 | 306 | 307 | int icmp_close(struct libicmp *obj) 308 | { 309 | if (!obj) 310 | return errno; 311 | 312 | if (obj->sd) 313 | close(obj->sd); 314 | 315 | free(obj->host); 316 | free(obj); 317 | 318 | return 0; 319 | } 320 | 321 | /** 322 | * Local Variables: 323 | * indent-tabs-mode: t 324 | * c-file-style: "linux" 325 | * End: 326 | */ 327 | --------------------------------------------------------------------------------