~ubuntu-branches/ubuntu/gutsy/libhdhomerun/gutsy

« back to all changes in this revision

Viewing changes to hdhomerun_dhcp.c

  • Committer: Bazaar Package Importer
  • Author(s): Mario Limonciello
  • Date: 2007-07-17 09:08:05 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20070717090805-3zk0nbh450t9q0zp
Tags: 0.20070716-0ubuntu1
* New upstream version (LP: #126420)
  - Introduces lightweight DHCP server
* Update license to LGPLv3.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * hdhomerun_dhcp.c
 
3
 *
 
4
 * Copyright � 2006-2007 Silicondust Engineering Ltd. <www.silicondust.com>.
 
5
 *
 
6
 * This library is free software; you can redistribute it and/or 
 
7
 * modify it under the terms of the GNU Lesser General Public
 
8
 * License as published by the Free Software Foundation; either
 
9
 * version 3 of the License, or (at your option) any later version.
 
10
 *
 
11
 * This library is distributed in the hope that it will be useful,
 
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
14
 * Lesser General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU Lesser General Public
 
17
 * License along with this library.  If not, see <http://www.gnu.org/licenses/>.
 
18
 */
 
19
 
 
20
#include "hdhomerun_os.h"
 
21
#include "hdhomerun_pkt.h"
 
22
#include "hdhomerun_dhcp.h"
 
23
 
 
24
struct dhcp_pkt_t {
 
25
        uint8_t bootp_message_type;
 
26
        uint8_t hardware_type;
 
27
        uint8_t hardware_address_length;
 
28
        uint8_t hops;
 
29
        uint32_t transaction_id;
 
30
        uint16_t seconds_elapsed;
 
31
        uint16_t bootp_flags;
 
32
        uint32_t client_ip;
 
33
        uint32_t your_ip;
 
34
        uint32_t next_server_ip;
 
35
        uint32_t relay_agent_ip;
 
36
        uint8_t client_mac[16];
 
37
        uint8_t server_host_name[64];
 
38
        uint8_t boot_file_name[128];
 
39
        uint32_t magic_cookie;
 
40
};
 
41
 
 
42
struct hdhomerun_dhcp_t {
 
43
        struct hdhomerun_dhcp_interface_t *intf_list;
 
44
        int sock;
 
45
        pthread_t thread;
 
46
        volatile bool_t terminate;
 
47
};
 
48
 
 
49
static THREAD_FUNC_PREFIX hdhomerun_dhcp_thread_execute(void *arg);
 
50
 
 
51
struct hdhomerun_dhcp_t *hdhomerun_dhcp_create(void)
 
52
{
 
53
        struct hdhomerun_dhcp_t *dhcp = (struct hdhomerun_dhcp_t *)calloc(1, sizeof(struct hdhomerun_dhcp_t));
 
54
        if (!dhcp) {
 
55
                return NULL;
 
56
        }
 
57
 
 
58
        if (pthread_create(&dhcp->thread, NULL, &hdhomerun_dhcp_thread_execute, dhcp) != 0) {
 
59
                free(dhcp);
 
60
                return NULL;
 
61
        }
 
62
 
 
63
        return dhcp;
 
64
}
 
65
 
 
66
void hdhomerun_dhcp_destroy(struct hdhomerun_dhcp_t *dhcp)
 
67
{
 
68
        dhcp->terminate = TRUE;
 
69
        pthread_join(dhcp->thread, NULL);
 
70
 
 
71
        if (dhcp->sock != -1) {
 
72
                close(dhcp->sock);
 
73
        }
 
74
 
 
75
        free(dhcp);
 
76
}
 
77
 
 
78
static bool_t hdhomerun_dhcp_create_sock(struct hdhomerun_dhcp_t *dhcp)
 
79
{
 
80
        /* Create socket. */
 
81
        int sock = (int)socket(AF_INET, SOCK_DGRAM, 0);
 
82
        if (sock == -1) {
 
83
                return FALSE;
 
84
        }
 
85
 
 
86
        /* Set timeout. */
 
87
        setsocktimeout(sock, SOL_SOCKET, SO_RCVTIMEO, 1000);
 
88
 
 
89
        /* Allow broadcast. */
 
90
        int sock_opt = 1;
 
91
        setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&sock_opt, sizeof(sock_opt));
 
92
 
 
93
        /* Bind socket. */
 
94
        struct sockaddr_in sock_addr;
 
95
        memset(&sock_addr, 0, sizeof(sock_addr));
 
96
        sock_addr.sin_family = AF_INET;
 
97
        sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);
 
98
        sock_addr.sin_port = htons(67);
 
99
        if (bind(sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != 0) {
 
100
                close(sock);
 
101
                return FALSE;
 
102
        }
 
103
 
 
104
        dhcp->sock = sock;
 
105
        return TRUE;
 
106
}
 
107
 
 
108
static void hdhomerun_dhcp_send(struct hdhomerun_dhcp_t *dhcp, uint8_t message_type, uint8_t *buffer)
 
109
{
 
110
        struct dhcp_pkt_t *pkt = (struct dhcp_pkt_t *)buffer;
 
111
 
 
112
        uint32_t remote_addr = 0xA9FE0000;
 
113
        remote_addr |= (uint32_t)pkt->client_mac[4] << 8;
 
114
        remote_addr |= (uint32_t)pkt->client_mac[5] << 0;
 
115
 
 
116
        pkt->bootp_message_type = 0x02;
 
117
        pkt->your_ip = htonl(remote_addr);
 
118
        pkt->next_server_ip = htonl(0xA9FEFFFF);
 
119
 
 
120
        uint8_t *ptr = buffer + sizeof(struct dhcp_pkt_t);
 
121
 
 
122
        hdhomerun_write_u8(&ptr, 53);
 
123
        hdhomerun_write_u8(&ptr, 1);
 
124
        hdhomerun_write_u8(&ptr, message_type);
 
125
 
 
126
        hdhomerun_write_u8(&ptr, 54);
 
127
        hdhomerun_write_u8(&ptr, 4);
 
128
        hdhomerun_write_u32(&ptr, 0xA9FEFFFF);
 
129
 
 
130
        hdhomerun_write_u8(&ptr, 51);
 
131
        hdhomerun_write_u8(&ptr, 4);
 
132
        hdhomerun_write_u32(&ptr, 7*24*60*60);
 
133
 
 
134
        hdhomerun_write_u8(&ptr, 1);
 
135
        hdhomerun_write_u8(&ptr, 4);
 
136
        hdhomerun_write_u32(&ptr, 0xFFFF0000);
 
137
 
 
138
        hdhomerun_write_u8(&ptr, 0xFF);
 
139
 
 
140
        while (ptr < buffer + 300) {
 
141
                hdhomerun_write_u8(&ptr, 0x00);
 
142
        }
 
143
 
 
144
        struct sockaddr_in sock_addr;
 
145
        memset(&sock_addr, 0, sizeof(sock_addr));
 
146
        sock_addr.sin_family = AF_INET;
 
147
        sock_addr.sin_addr.s_addr = htonl(0xFFFFFFFF);
 
148
        sock_addr.sin_port = htons(68);
 
149
 
 
150
        sendto(dhcp->sock, (char *)buffer, (int)(ptr - buffer), 0, (struct sockaddr *)&sock_addr, sizeof(sock_addr));
 
151
}
 
152
 
 
153
static void hdhomerun_dhcp_recv(struct hdhomerun_dhcp_t *dhcp, uint8_t *buffer, uint8_t *end)
 
154
{
 
155
        uint8_t *ptr = buffer + sizeof(struct dhcp_pkt_t);
 
156
        if (ptr > end) {
 
157
                return;
 
158
        }
 
159
 
 
160
        struct dhcp_pkt_t *pkt = (struct dhcp_pkt_t *)buffer;
 
161
        if (ntohl(pkt->magic_cookie) != 0x63825363) {
 
162
                return;
 
163
        }
 
164
 
 
165
        static uint8_t vendor[3] = {0x00, 0x18, 0xDD};
 
166
        if (memcmp(pkt->client_mac, vendor, 3) != 0) {
 
167
                return;
 
168
        }
 
169
 
 
170
        if (ptr + 3 > end) {
 
171
                return;
 
172
        }
 
173
        if (hdhomerun_read_u8(&ptr) != 53) {
 
174
                return;
 
175
        }
 
176
        if (hdhomerun_read_u8(&ptr) != 1) {
 
177
                return;
 
178
        }
 
179
        uint8_t message_type_val = hdhomerun_read_u8(&ptr);
 
180
 
 
181
        switch (message_type_val) {
 
182
        case 0x01:
 
183
                hdhomerun_dhcp_send(dhcp, 0x02, buffer);
 
184
                break;
 
185
        case 0x03:
 
186
                hdhomerun_dhcp_send(dhcp, 0x05, buffer);
 
187
                break;
 
188
        default:
 
189
                return;
 
190
        }
 
191
}
 
192
 
 
193
static THREAD_FUNC_PREFIX hdhomerun_dhcp_thread_execute(void *arg)
 
194
{
 
195
        struct hdhomerun_dhcp_t *dhcp = (struct hdhomerun_dhcp_t *)arg;
 
196
 
 
197
        while (1) {
 
198
                if (dhcp->terminate) {
 
199
                        return NULL;
 
200
                }
 
201
 
 
202
                if (dhcp->sock == -1) {
 
203
                        if (!hdhomerun_dhcp_create_sock(dhcp)) {
 
204
                                sleep(1);
 
205
                                continue;
 
206
                        }
 
207
                }
 
208
 
 
209
                uint8_t buffer[1460];
 
210
                int length = recv(dhcp->sock, (char *)buffer, sizeof(buffer), 0);
 
211
                if (length <= 0) {
 
212
                        if (sock_getlasterror_socktimeout) {
 
213
                                continue;
 
214
                        }
 
215
                        close(dhcp->sock);
 
216
                        dhcp->sock = -1;
 
217
                        continue;
 
218
                }
 
219
 
 
220
                hdhomerun_dhcp_recv(dhcp, buffer, buffer + length);
 
221
        }
 
222
}