00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "hdhomerun.h"
00021 #include "hdhomerun_dhcp.h"
00022
00023 struct dhcp_hdr_t {
00024 uint8_t bootp_message_type;
00025 uint8_t hardware_type;
00026 uint8_t hardware_address_length;
00027 uint8_t hops;
00028 uint32_t transaction_id;
00029 uint16_t seconds_elapsed;
00030 uint16_t bootp_flags;
00031 uint32_t client_ip;
00032 uint32_t your_ip;
00033 uint32_t next_server_ip;
00034 uint32_t relay_agent_ip;
00035 uint8_t client_mac[16];
00036 uint8_t server_host_name[64];
00037 uint8_t boot_file_name[128];
00038 uint32_t magic_cookie;
00039 };
00040
00041 struct hdhomerun_dhcp_t {
00042 int sock;
00043 uint32_t local_address;
00044 pthread_t thread;
00045 volatile bool_t terminate;
00046 };
00047
00048 static THREAD_FUNC_PREFIX hdhomerun_dhcp_thread_execute(void *arg);
00049
00050 struct hdhomerun_dhcp_t *hdhomerun_dhcp_create(uint32_t bind_address)
00051 {
00052 if (bind_address != 0) {
00053 if ((bind_address & 0xFFFF0000) != 0xA9FE0000) {
00054 return NULL;
00055 }
00056 }
00057
00058
00059 int sock = (int)socket(AF_INET, SOCK_DGRAM, 0);
00060 if (sock == -1) {
00061 return NULL;
00062 }
00063
00064
00065 setsocktimeout(sock, SOL_SOCKET, SO_RCVTIMEO, 1000);
00066
00067
00068 int sock_opt = 1;
00069 setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&sock_opt, sizeof(sock_opt));
00070
00071
00072 sock_opt = 1;
00073 setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (char *)&sock_opt, sizeof(sock_opt));
00074
00075
00076 struct sockaddr_in sock_addr;
00077 memset(&sock_addr, 0, sizeof(sock_addr));
00078 sock_addr.sin_family = AF_INET;
00079 sock_addr.sin_addr.s_addr = htonl(bind_address);
00080 sock_addr.sin_port = htons(67);
00081 if (bind(sock, (struct sockaddr *)&sock_addr, sizeof(sock_addr)) != 0) {
00082 close(sock);
00083 return NULL;
00084 }
00085
00086
00087 struct hdhomerun_dhcp_t *dhcp = (struct hdhomerun_dhcp_t *)calloc(1, sizeof(struct hdhomerun_dhcp_t));
00088 if (!dhcp) {
00089 close(sock);
00090 return NULL;
00091 }
00092
00093 dhcp->sock = sock;
00094
00095 if (bind_address != 0) {
00096 dhcp->local_address = bind_address;
00097 } else {
00098 dhcp->local_address = 0xA9FEFFFF;
00099 }
00100
00101
00102 if (pthread_create(&dhcp->thread, NULL, &hdhomerun_dhcp_thread_execute, dhcp) != 0) {
00103 close(sock);
00104 free(dhcp);
00105 return NULL;
00106 }
00107
00108
00109 return dhcp;
00110 }
00111
00112 void hdhomerun_dhcp_destroy(struct hdhomerun_dhcp_t *dhcp)
00113 {
00114 dhcp->terminate = TRUE;
00115 pthread_join(dhcp->thread, NULL);
00116
00117 close(dhcp->sock);
00118 free(dhcp);
00119 }
00120
00121 static void hdhomerun_dhcp_send(struct hdhomerun_dhcp_t *dhcp, uint8_t message_type, struct hdhomerun_pkt_t *pkt)
00122 {
00123 pkt->pos = pkt->start;
00124 struct dhcp_hdr_t *hdr = (struct dhcp_hdr_t *)pkt->pos;
00125 pkt->pos += sizeof(struct dhcp_hdr_t);
00126 pkt->end = pkt->pos;
00127
00128 uint32_t remote_addr = 0xA9FE0000;
00129 remote_addr |= (uint32_t)hdr->client_mac[4] << 8;
00130 remote_addr |= (uint32_t)hdr->client_mac[5] << 0;
00131 if ((remote_addr == 0xA9FE0000) || (remote_addr == 0xA9FEFFFF)) {
00132 remote_addr = 0xA9FE8080;
00133 }
00134
00135 hdr->bootp_message_type = 0x02;
00136 hdr->your_ip = htonl(remote_addr);
00137 hdr->next_server_ip = htonl(0x00000000);
00138
00139 hdhomerun_pkt_write_u8(pkt, 53);
00140 hdhomerun_pkt_write_u8(pkt, 1);
00141 hdhomerun_pkt_write_u8(pkt, message_type);
00142
00143 hdhomerun_pkt_write_u8(pkt, 54);
00144 hdhomerun_pkt_write_u8(pkt, 4);
00145 hdhomerun_pkt_write_u32(pkt, dhcp->local_address);
00146
00147 hdhomerun_pkt_write_u8(pkt, 51);
00148 hdhomerun_pkt_write_u8(pkt, 4);
00149 hdhomerun_pkt_write_u32(pkt, 7*24*60*60);
00150
00151 hdhomerun_pkt_write_u8(pkt, 1);
00152 hdhomerun_pkt_write_u8(pkt, 4);
00153 hdhomerun_pkt_write_u32(pkt, 0xFFFF0000);
00154
00155 hdhomerun_pkt_write_u8(pkt, 0xFF);
00156
00157 while (pkt->pos < pkt->start + 300) {
00158 hdhomerun_pkt_write_u8(pkt, 0x00);
00159 }
00160
00161 struct sockaddr_in sock_addr;
00162 memset(&sock_addr, 0, sizeof(sock_addr));
00163 sock_addr.sin_family = AF_INET;
00164 sock_addr.sin_addr.s_addr = htonl(0xFFFFFFFF);
00165 sock_addr.sin_port = htons(68);
00166
00167 sendto(dhcp->sock, (char *)pkt->start, (int)(pkt->end - pkt->start), 0, (struct sockaddr *)&sock_addr, sizeof(sock_addr));
00168 }
00169
00170 static void hdhomerun_dhcp_recv(struct hdhomerun_dhcp_t *dhcp, struct hdhomerun_pkt_t *pkt)
00171 {
00172 pkt->pos = pkt->start;
00173 struct dhcp_hdr_t *hdr = (struct dhcp_hdr_t *)pkt->pos;
00174 pkt->pos += sizeof(struct dhcp_hdr_t);
00175 if (pkt->pos > pkt->end) {
00176 return;
00177 }
00178
00179 if (ntohl(hdr->magic_cookie) != 0x63825363) {
00180 return;
00181 }
00182
00183 static uint8_t vendor[3] = {0x00, 0x18, 0xDD};
00184 if (memcmp(hdr->client_mac, vendor, 3) != 0) {
00185 return;
00186 }
00187
00188 if (pkt->pos + 3 > pkt->end) {
00189 return;
00190 }
00191 if (hdhomerun_pkt_read_u8(pkt) != 53) {
00192 return;
00193 }
00194 if (hdhomerun_pkt_read_u8(pkt) != 1) {
00195 return;
00196 }
00197 uint8_t message_type_val = hdhomerun_pkt_read_u8(pkt);
00198
00199 switch (message_type_val) {
00200 case 0x01:
00201 hdhomerun_dhcp_send(dhcp, 0x02, pkt);
00202 break;
00203 case 0x03:
00204 hdhomerun_dhcp_send(dhcp, 0x05, pkt);
00205 break;
00206 default:
00207 return;
00208 }
00209 }
00210
00211 static THREAD_FUNC_PREFIX hdhomerun_dhcp_thread_execute(void *arg)
00212 {
00213 struct hdhomerun_dhcp_t *dhcp = (struct hdhomerun_dhcp_t *)arg;
00214 struct hdhomerun_pkt_t pkt_inst;
00215
00216 while (1) {
00217 if (dhcp->terminate) {
00218 return NULL;
00219 }
00220
00221 struct hdhomerun_pkt_t *pkt = &pkt_inst;
00222 hdhomerun_pkt_reset(pkt);
00223
00224 int rx_length = recv(dhcp->sock, (char *)pkt->end, (int)(pkt->limit - pkt->end), 0);
00225 if (rx_length <= 0) {
00226 if (!sock_getlasterror_socktimeout) {
00227 sleep(1);
00228 }
00229 continue;
00230 }
00231 pkt->end += rx_length;
00232
00233 hdhomerun_dhcp_recv(dhcp, pkt);
00234 }
00235 }