Skip to content

Commit 566ce86

Browse files
Improve ap example (#233)
* Add dns server The access point example is a bit complicated to use as you have to open a browser and enter a url with the IP address of the device Add a simple dns server that can be used to make the access point behave like a captive portal. All DNS requests point back to the gateway IP. * Improve AP example The web page /ledtest gives you a web page with an option to turn the led on and off Start the DNS server and redirect all HTTP requests to this page. Improve the code so it can handle more than one request at a time. Fixes: #232
1 parent a605c65 commit 566ce86

File tree

5 files changed

+461
-56
lines changed

5 files changed

+461
-56
lines changed

pico_w/access_point/CMakeLists.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
add_executable(picow_access_point_background
22
picow_access_point.c
33
dhcpserver/dhcpserver.c
4+
dnsserver/dnsserver.c
45
)
56

67
target_include_directories(picow_access_point_background PRIVATE
78
${CMAKE_CURRENT_LIST_DIR}
89
${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts
910
${CMAKE_CURRENT_LIST_DIR}/dhcpserver
11+
${CMAKE_CURRENT_LIST_DIR}/dnsserver
1012
)
1113

1214
target_link_libraries(picow_access_point_background
@@ -19,15 +21,16 @@ pico_add_extra_outputs(picow_access_point_background)
1921
add_executable(picow_access_point_poll
2022
picow_access_point.c
2123
dhcpserver/dhcpserver.c
24+
dnsserver/dnsserver.c
2225
)
2326
target_include_directories(picow_access_point_poll PRIVATE
2427
${CMAKE_CURRENT_LIST_DIR}
2528
${CMAKE_CURRENT_LIST_DIR}/.. # for our common lwipopts
2629
${CMAKE_CURRENT_LIST_DIR}/dhcpserver
30+
${CMAKE_CURRENT_LIST_DIR}/dnsserver
2731
)
2832
target_link_libraries(picow_access_point_poll
2933
pico_cyw43_arch_lwip_poll
3034
pico_stdlib
3135
)
3236
pico_add_extra_outputs(picow_access_point_poll)
33-

pico_w/access_point/dhcpserver/dhcpserver.c

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,6 @@
6363
#define PORT_DHCP_SERVER (67)
6464
#define PORT_DHCP_CLIENT (68)
6565

66-
#define DEFAULT_DNS MAKE_IP4(8, 8, 8, 8)
6766
#define DEFAULT_LEASE_TIME_S (24 * 60 * 60) // in seconds
6867

6968
#define MAC_LEN (6)
@@ -274,7 +273,7 @@ static void dhcp_server_process(void *arg, struct udp_pcb *upcb, struct pbuf *p,
274273
opt_write_n(&opt, DHCP_OPT_SERVER_ID, 4, &ip4_addr_get_u32(ip_2_ip4(&d->ip)));
275274
opt_write_n(&opt, DHCP_OPT_SUBNET_MASK, 4, &ip4_addr_get_u32(ip_2_ip4(&d->nm)));
276275
opt_write_n(&opt, DHCP_OPT_ROUTER, 4, &ip4_addr_get_u32(ip_2_ip4(&d->ip))); // aka gateway; can have mulitple addresses
277-
opt_write_u32(&opt, DHCP_OPT_DNS, DEFAULT_DNS); // can have mulitple addresses
276+
opt_write_n(&opt, DHCP_OPT_DNS, 4, &ip4_addr_get_u32(ip_2_ip4(&d->ip))); // this server is the dns
278277
opt_write_u32(&opt, DHCP_OPT_IP_LEASE_TIME, DEFAULT_LEASE_TIME_S);
279278
*opt++ = DHCP_OPT_END;
280279
dhcp_socket_sendto(&d->udp, &dhcp_msg, opt - (uint8_t *)&dhcp_msg, 0xffffffff, PORT_DHCP_CLIENT);
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
/**
2+
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
3+
*
4+
* SPDX-License-Identifier: BSD-3-Clause
5+
*/
6+
7+
#include <stdio.h>
8+
#include <string.h>
9+
#include <errno.h>
10+
#include <assert.h>
11+
#include <stdbool.h>
12+
13+
#include "dnsserver.h"
14+
#include "lwip/udp.h"
15+
16+
#define PORT_DNS_SERVER 53
17+
#define DUMP_DATA 0
18+
19+
#define DEBUG_printf(...)
20+
#define ERROR_printf printf
21+
22+
typedef struct dns_header_t_ {
23+
uint16_t id;
24+
uint16_t flags;
25+
uint16_t question_count;
26+
uint16_t answer_record_count;
27+
uint16_t authority_record_count;
28+
uint16_t additional_record_count;
29+
} dns_header_t;
30+
31+
#define MAX_DNS_MSG_SIZE 300
32+
33+
static int dns_socket_new_dgram(struct udp_pcb **udp, void *cb_data, udp_recv_fn cb_udp_recv) {
34+
*udp = udp_new();
35+
if (*udp == NULL) {
36+
return -ENOMEM;
37+
}
38+
udp_recv(*udp, cb_udp_recv, (void *)cb_data);
39+
return ERR_OK;
40+
}
41+
42+
static void dns_socket_free(struct udp_pcb **udp) {
43+
if (*udp != NULL) {
44+
udp_remove(*udp);
45+
*udp = NULL;
46+
}
47+
}
48+
49+
static int dns_socket_bind(struct udp_pcb **udp, uint32_t ip, uint16_t port) {
50+
ip_addr_t addr;
51+
IP4_ADDR(&addr, ip >> 24 & 0xff, ip >> 16 & 0xff, ip >> 8 & 0xff, ip & 0xff);
52+
err_t err = udp_bind(*udp, &addr, port);
53+
if (err != ERR_OK) {
54+
ERROR_printf("dns failed to bind to port %u: %d", port, err);
55+
assert(false);
56+
}
57+
return err;
58+
}
59+
60+
#if DUMP_DATA
61+
static void dump_bytes(const uint8_t *bptr, uint32_t len) {
62+
unsigned int i = 0;
63+
64+
for (i = 0; i < len;) {
65+
if ((i & 0x0f) == 0) {
66+
printf("\n");
67+
} else if ((i & 0x07) == 0) {
68+
printf(" ");
69+
}
70+
printf("%02x ", bptr[i++]);
71+
}
72+
printf("\n");
73+
}
74+
#endif
75+
76+
static int dns_socket_sendto(struct udp_pcb **udp, const void *buf, size_t len, const ip_addr_t *dest, uint16_t port) {
77+
if (len > 0xffff) {
78+
len = 0xffff;
79+
}
80+
81+
struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, len, PBUF_RAM);
82+
if (p == NULL) {
83+
ERROR_printf("DNS: Failed to send message out of memory\n");
84+
return -ENOMEM;
85+
}
86+
87+
memcpy(p->payload, buf, len);
88+
err_t err = udp_sendto(*udp, p, dest, port);
89+
90+
pbuf_free(p);
91+
92+
if (err != ERR_OK) {
93+
ERROR_printf("DNS: Failed to send message %d\n", err);
94+
return err;
95+
}
96+
97+
#if DUMP_DATA
98+
dump_bytes(buf, len);
99+
#endif
100+
return len;
101+
}
102+
103+
static void dns_server_process(void *arg, struct udp_pcb *upcb, struct pbuf *p, const ip_addr_t *src_addr, u16_t src_port) {
104+
dns_server_t *d = arg;
105+
DEBUG_printf("dns_server_process %u\n", p->tot_len);
106+
107+
uint8_t dns_msg[MAX_DNS_MSG_SIZE];
108+
dns_header_t *dns_hdr = (dns_header_t*)dns_msg;
109+
110+
size_t msg_len = pbuf_copy_partial(p, dns_msg, sizeof(dns_msg), 0);
111+
if (msg_len < sizeof(dns_header_t)) {
112+
goto ignore_request;
113+
}
114+
115+
#if DUMP_DATA
116+
dump_bytes(dns_msg, msg_len);
117+
#endif
118+
119+
uint16_t flags = lwip_ntohs(dns_hdr->flags);
120+
uint16_t question_count = lwip_ntohs(dns_hdr->question_count);
121+
122+
DEBUG_printf("len %d\n", msg_len);
123+
DEBUG_printf("dns flags 0x%x\n", flags);
124+
DEBUG_printf("dns question count 0x%x\n", question_count);
125+
126+
// flags from rfc1035
127+
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
128+
// |QR| Opcode |AA|TC|RD|RA| Z | RCODE |
129+
// +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
130+
131+
// Check QR indicates a query
132+
if (((flags >> 15) & 0x1) != 0) {
133+
DEBUG_printf("Ignoring non-query\n");
134+
goto ignore_request;
135+
}
136+
137+
// Check for standard query
138+
if (((flags >> 11) & 0xf) != 0) {
139+
DEBUG_printf("Ignoring non-standard query\n");
140+
goto ignore_request;
141+
}
142+
143+
// Check question count
144+
if (question_count < 1) {
145+
DEBUG_printf("Invalid question count\n");
146+
goto ignore_request;
147+
}
148+
149+
// Print the question
150+
DEBUG_printf("question: ");
151+
const uint8_t *question_ptr_start = dns_msg + sizeof(dns_header_t);
152+
const uint8_t *question_ptr_end = dns_msg + msg_len;
153+
const uint8_t *question_ptr = question_ptr_start;
154+
while(question_ptr < question_ptr_end) {
155+
if (*question_ptr == 0) {
156+
question_ptr++;
157+
break;
158+
} else {
159+
if (question_ptr > question_ptr_start) {
160+
DEBUG_printf(".");
161+
}
162+
int label_len = *question_ptr++;
163+
if (label_len > 63) {
164+
DEBUG_printf("Invalid label\n");
165+
goto ignore_request;
166+
}
167+
DEBUG_printf("%.*s", label_len, question_ptr);
168+
question_ptr += label_len;
169+
}
170+
}
171+
DEBUG_printf("\n");
172+
173+
// Check question length
174+
if (question_ptr - question_ptr_start > 255) {
175+
DEBUG_printf("Invalid question length\n");
176+
goto ignore_request;
177+
}
178+
179+
// Skip QNAME and QTYPE
180+
question_ptr += 4;
181+
182+
// Generate answer
183+
uint8_t *answer_ptr = dns_msg + (question_ptr - dns_msg);
184+
*answer_ptr++ = 0xc0; // pointer
185+
*answer_ptr++ = question_ptr_start - dns_msg; // pointer to question
186+
187+
*answer_ptr++ = 0;
188+
*answer_ptr++ = 1; // host address
189+
190+
*answer_ptr++ = 0;
191+
*answer_ptr++ = 1; // Internet class
192+
193+
*answer_ptr++ = 0;
194+
*answer_ptr++ = 0;
195+
*answer_ptr++ = 0;
196+
*answer_ptr++ = 60; // ttl 60s
197+
198+
*answer_ptr++ = 0;
199+
*answer_ptr++ = 4; // length
200+
memcpy(answer_ptr, &d->ip.addr, 4); // use our address
201+
answer_ptr += 4;
202+
203+
dns_hdr->flags = lwip_htons(
204+
0x1 << 15 | // QR = response
205+
0x1 << 10 | // AA = authoritive
206+
0x1 << 7); // RA = authenticated
207+
dns_hdr->question_count = lwip_htons(1);
208+
dns_hdr->answer_record_count = lwip_htons(1);
209+
dns_hdr->authority_record_count = 0;
210+
dns_hdr->additional_record_count = 0;
211+
212+
// Send the reply
213+
DEBUG_printf("Sending %d byte reply to %s:%d\n", answer_ptr - dns_msg, ipaddr_ntoa(src_addr), src_port);
214+
dns_socket_sendto(&d->udp, &dns_msg, answer_ptr - dns_msg, src_addr, src_port);
215+
216+
ignore_request:
217+
pbuf_free(p);
218+
}
219+
220+
void dns_server_init(dns_server_t *d, ip_addr_t *ip) {
221+
if (dns_socket_new_dgram(&d->udp, d, dns_server_process) != ERR_OK) {
222+
DEBUG_printf("dns server failed to start\n");
223+
return;
224+
}
225+
if (dns_socket_bind(&d->udp, 0, PORT_DNS_SERVER) != ERR_OK) {
226+
DEBUG_printf("dns server failed to bind\n");
227+
return;
228+
}
229+
ip_addr_copy(d->ip, *ip);
230+
DEBUG_printf("dns server listening on port %d\n", PORT_DNS_SERVER);
231+
}
232+
233+
void dns_server_deinit(dns_server_t *d) {
234+
dns_socket_free(&d->udp);
235+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/**
2+
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
3+
*
4+
* SPDX-License-Identifier: BSD-3-Clause
5+
*/
6+
7+
#ifndef _DNSSERVER_H_
8+
#define _DNSSERVER_H_
9+
10+
#include "lwip/ip_addr.h"
11+
12+
typedef struct dns_server_t_ {
13+
struct udp_pcb *udp;
14+
ip_addr_t ip;
15+
} dns_server_t;
16+
17+
void dns_server_init(dns_server_t *d, ip_addr_t *ip);
18+
void dns_server_deinit(dns_server_t *d);
19+
20+
#endif

0 commit comments

Comments
 (0)