Skip to content

Commit a0a3754

Browse files
committed
add socket state callback example
1 parent 8dee22c commit a0a3754

File tree

1 file changed

+188
-5
lines changed

1 file changed

+188
-5
lines changed

docs.md

Lines changed: 188 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ layout: page
55
---
66

77
- [Manpages](#manpages)
8-
- [Example](#example)
8+
- [Examples](#examples)
99

1010
c-ares provides a set of library functions, datatypes, and enumerations
1111
which integrators will use for their implementations. When you install c-ares,
@@ -19,10 +19,11 @@ you get man pages which describe their use and meanings.
1919
- [{{ my_page.path | replace: "docs/", "" | replace: ".html", "" }}](/{{ my_page.path }})
2020
{% endfor -%}
2121

22-
## Example
23-
A simple name resolve might look like:
22+
## Examples
2423

25-
`example.c`:
24+
### Event Thread example (recommended)
25+
26+
`example_ev.c`:
2627
{% highlight C %}
2728
#include <stdio.h>
2829
#include <string.h>
@@ -110,8 +111,190 @@ int main(int argc, char **argv)
110111

111112
Compilation:
112113
{% highlight bash %}
113-
cc -I/usr/local/include -o example example.c -Wl,-rpath /usr/local/lib -lcares
114+
cc -I/usr/local/include -o example_ev example_ev.c -Wl,-rpath /usr/local/lib -lcares
114115
{% endhighlight %}
115116

117+
### Sock State Callback example
118+
119+
`example_ss.c`:
120+
{% highlight C %}
121+
#include <stdio.h>
122+
#include <string.h>
123+
#include <stdlib.h>
124+
#include <poll.h>
125+
#include <ares.h>
126+
127+
typedef struct {
128+
ares_channel_t *channel;
129+
struct pollfd *fds;
130+
size_t nfds;
131+
} dnsstate_t;
132+
133+
void sock_state_cb(void *data, ares_socket_t socket_fd, int readable, int writable)
134+
{
135+
dnsstate_t *state = data;
136+
size_t idx;
137+
138+
/* Find match */
139+
for (idx=0; idx<state->nfds; idx++) {
140+
if (state->fds[idx].fd == socket_fd) {
141+
break;
142+
}
143+
}
144+
145+
/* Not found */
146+
if (idx >= state->nfds) {
147+
/* Do nothing */
148+
if (!readable && !writable) {
149+
return;
150+
}
151+
152+
/* Add */
153+
state->nfds++;
154+
state->fds = realloc(state->fds, sizeof(*state->fds) * state->nfds);
155+
} else {
156+
/* Remove */
157+
if (!readable && !writable) {
158+
memmove(&state->fds[idx], &state->fds[idx+1],
159+
sizeof(*state->fds) * (state->nfds - idx - 1));
160+
state->nfds--;
161+
return;
162+
}
163+
}
164+
165+
state->fds[idx].fd = socket_fd;
166+
state->fds[idx].events = 0;
167+
if (readable) {
168+
state->fds[idx].events |= POLL_IN;
169+
}
170+
if (writable) {
171+
state->fds[idx].events |= POLLOUT;
172+
}
173+
}
174+
175+
void process(dnsstate_t *state)
176+
{
177+
struct timeval tv;
178+
179+
while (1) {
180+
int rv;
181+
int timeout;
182+
size_t i;
183+
184+
/* Since we don't have any other program state to wait on, we'll just
185+
* stop looping when we know there are no remaining queries, which is
186+
* easily indicated by ares_timeout() returning NULL when maxtv is NULL */
187+
if (ares_timeout(state->channel, NULL, &tv) == NULL) {
188+
break;
189+
}
190+
191+
timeout = tv.tv_sec * 1000 + tv.tv_usec / 1000;
192+
193+
rv = poll(state->fds, state->nfds, timeout);
194+
if (rv < 0) {
195+
continue;
196+
} else if (rv == 0) {
197+
/* Process timeouts */
198+
ares_process_fd(state->channel, ARES_SOCKET_BAD, ARES_SOCKET_BAD);
199+
continue;
200+
}
201+
202+
for (i=0; i<state->nfds; i++) {
203+
if (state->fds[i].revents == 0) {
204+
continue;
205+
}
206+
207+
/* Notify about read/write events per FD */
208+
ares_process_fd(state->channel,
209+
(state->fds[i].revents & (POLLERR|POLLHUP|POLLIN))?
210+
state->fds[i].fd:ARES_SOCKET_BAD,
211+
(state->fds[i].revents & POLLOUT)?
212+
state->fds[i].fd:ARES_SOCKET_BAD);
213+
}
214+
}
215+
}
216+
217+
/* Callback that is called when DNS query is finished */
218+
static void addrinfo_cb(void *arg, int status, int timeouts,
219+
struct ares_addrinfo *result)
220+
{
221+
(void)arg; /* Example does not use user context */
222+
printf("Result: %s, timeouts: %d\n", ares_strerror(status), timeouts);
223+
224+
if (result) {
225+
struct ares_addrinfo_node *node;
226+
for (node = result->nodes; node != NULL; node = node->ai_next) {
227+
char addr_buf[64] = "";
228+
const void *ptr = NULL;
229+
if (node->ai_family == AF_INET) {
230+
const struct sockaddr_in *in_addr =
231+
(const struct sockaddr_in *)((void *)node->ai_addr);
232+
ptr = &in_addr->sin_addr;
233+
} else if (node->ai_family == AF_INET6) {
234+
const struct sockaddr_in6 *in_addr =
235+
(const struct sockaddr_in6 *)((void *)node->ai_addr);
236+
ptr = &in_addr->sin6_addr;
237+
} else {
238+
continue;
239+
}
240+
ares_inet_ntop(node->ai_family, ptr, addr_buf, sizeof(addr_buf));
241+
printf("Addr: %s\n", addr_buf);
242+
}
243+
}
244+
ares_freeaddrinfo(result);
245+
}
246+
247+
int main(int argc, char **argv)
248+
{
249+
dnsstate_t state;
250+
struct ares_options options;
251+
int optmask = 0;
252+
struct ares_addrinfo_hints hints;
253+
254+
memset(&state, 0, sizeof(state));
116255

256+
if (argc != 2) {
257+
printf("Usage: %s domain\n", argv[0]);
258+
return 1;
259+
}
260+
261+
/* Initialize library */
262+
ares_library_init(ARES_LIB_INIT_ALL);
117263

264+
/* Enable sock state callbacks, we should not use ares_fds() or ares_getsock()
265+
* in modern implementations. */
266+
memset(&options, 0, sizeof(options));
267+
optmask |= ARES_OPT_SOCK_STATE_CB;
268+
options.sock_state_cb = sock_state_cb;
269+
options.sock_state_cb_data = &state;
270+
271+
/* Initialize channel to run queries, a single channel can accept unlimited
272+
* queries */
273+
if (ares_init_options(&state.channel, &options, optmask) != ARES_SUCCESS) {
274+
printf("c-ares initialization issue\n");
275+
return 1;
276+
}
277+
278+
/* Perform an IPv4 and IPv6 request for the provided domain name */
279+
memset(&hints, 0, sizeof(hints));
280+
hints.ai_family = AF_UNSPEC;
281+
hints.ai_flags = ARES_AI_CANONNAME;
282+
ares_getaddrinfo(state.channel, argv[1], NULL, &hints, addrinfo_cb,
283+
NULL /* user context not specified */);
284+
285+
/* Wait until no more requests are left to be processed */
286+
process(&state);
287+
288+
/* Cleanup */
289+
ares_destroy(state.channel);
290+
free(state.fds);
291+
292+
ares_library_cleanup();
293+
return 0;
294+
}
295+
{% endhighlight %}
296+
297+
Compilation:
298+
{% highlight bash %}
299+
cc -I/usr/local/include -o example_ss example_ss.c -Wl,-rpath /usr/local/lib -lcares
300+
{% endhighlight %}

0 commit comments

Comments
 (0)