|
| 1 | +/* Klient sluzby NBNS |
| 2 | + */ |
| 3 | + |
| 4 | +#include "ESP8266NetBIOS.h" |
| 5 | + |
| 6 | +#include <functional> |
| 7 | + |
| 8 | +extern "C" { |
| 9 | +#include "osapi.h" |
| 10 | +#include "ets_sys.h" |
| 11 | +#include "user_interface.h" |
| 12 | +} |
| 13 | + |
| 14 | +#include "lwip/opt.h" |
| 15 | +#include "lwip/inet.h" |
| 16 | +#include "lwip/udp.h" |
| 17 | + |
| 18 | +#define NBNSQ_TYPE_NB (0x0020) |
| 19 | +#define NBNSQ_CLASS_IN (0x0001) |
| 20 | + |
| 21 | +// Definice struktury NBNS dotazu (alespon veci, ktere jsem vypozoroval): |
| 22 | +struct NBNSQUESTION { |
| 23 | + uint16_t NBNSQ_ID; // ID dotazu |
| 24 | + uint8_t NBNSQ_FLAGS1; |
| 25 | + uint8_t NBNSQ_FLAGS2; |
| 26 | + uint16_t NBNSQ_QUESTIONCOUNT; |
| 27 | + uint16_t NBNSQ_ANSWERCOUNT; |
| 28 | + uint16_t NBNSQ_AUTHORITYCOUNT; |
| 29 | + uint16_t NBNSQ_ADDITIONALRECORDCOUNT; |
| 30 | + uint8_t NBNSQ_NAMESIZE; // delka nasledujiciho retezce |
| 31 | + char NBNSQ_NAME[32+1]; // POZOR!!! mozna tato polozka muze byt ruzne dlouha |
| 32 | + uint16_t NBNSQ_TYPE; |
| 33 | + uint16_t NBNSQ_CLASS; |
| 34 | +} __attribute__((packed)); |
| 35 | + |
| 36 | +// Definice struktury NBNS odpovedi (stejne jako u dotazu) |
| 37 | +struct NBNSANSWER { |
| 38 | + uint16_t NBNSA_ID; // ID dotazu |
| 39 | + uint8_t NBNSA_FLAGS1; |
| 40 | + uint8_t NBNSA_FLAGS2; |
| 41 | + uint16_t NBNSA_QUESTIONCOUNT; |
| 42 | + uint16_t NBNSA_ANSWERCOUNT; |
| 43 | + uint16_t NBNSA_AUTHORITYCOUNT; |
| 44 | + uint16_t NBNSA_ADDITIONALRECORDCOUNT; |
| 45 | + uint8_t NBNSA_NAMESIZE; // delka nasledujiciho retezce |
| 46 | + char NBNSA_NAME[32 + 1]; // POZOR!!! mozna tato polozka muze byt ruzne dlouha |
| 47 | + uint16_t NBNSA_TYPE; |
| 48 | + uint16_t NBNSA_CLASS; |
| 49 | + uint32_t NBNSA_TIMETOLIVE; |
| 50 | + uint16_t NBNSA_LENGTH; |
| 51 | + uint16_t NBNSA_NODEFLAGS; // POZOR!!! tady si nejsem moc jisty |
| 52 | + uint32_t NBNSA_NODEADDRESS; |
| 53 | +} __attribute__((packed)); |
| 54 | + |
| 55 | +// Definice struktury NBNS odpovedi na dotaz na jmeno |
| 56 | +struct NBNSANSWERN { |
| 57 | + uint16_t NBNSAN_ID; // ID dotazu |
| 58 | + uint8_t NBNSAN_FLAGS1; |
| 59 | + uint8_t NBNSAN_FLAGS2; |
| 60 | + uint16_t NBNSAN_QUESTIONCOUNT; |
| 61 | + uint16_t NBNSAN_ANSWERCOUNT; |
| 62 | + uint16_t NBNSAN_AUTHORITYCOUNT; |
| 63 | + uint16_t NBNSAN_ADDITIONALRECORDCOUNT; |
| 64 | + uint8_t NBNSAN_NAMESIZE; // delka nasledujiciho retezce |
| 65 | + char NBNSAN_NAME[32 + 1]; // POZOR!!! mozna tato polozka muze byt ruzne dlouha |
| 66 | + uint16_t NBNSAN_TYPE; |
| 67 | + uint16_t NBNSAN_CLASS; |
| 68 | + uint32_t NBNSAN_TIMETOLIVE; |
| 69 | + uint16_t NBNSAN_LENGTH; |
| 70 | + uint8_t NBNSAN_NUMBER; // number of names |
| 71 | + char NBNSAN_NNAME[15]; // jmeno nodu |
| 72 | + uint8_t NBNSAN_NTYPE; // typ jmena |
| 73 | + uint16_t NBNSAN_NFLAGS; // node flags |
| 74 | +} __attribute__((packed)); |
| 75 | + |
| 76 | +/** Metoda pro ziskani jmena z kodovani NETBIOS. |
| 77 | + * \param nbname Ukazatel na jmeno v NETBIOS kodovani. |
| 78 | + * \param name Ukazatel na misto, kam prevadime jmeno. |
| 79 | + * \param maxlen Maximalni pocet znaku v nbname. |
| 80 | + */ |
| 81 | +void ESP8266NetBIOS::_getnbname(char *nbname, char *name, uint8_t maxlen) |
| 82 | +{ |
| 83 | + uint8_t b; |
| 84 | + uint8_t c = 0; |
| 85 | + |
| 86 | + while ((*nbname != 0x0) && (c < maxlen)) { |
| 87 | + b = (*nbname++ - 'A') << 4; // opravime nibble a prevedeme ho do vyssich bitu |
| 88 | + c++; // pocitame pocet odebranych bytu |
| 89 | + if (*nbname != 0x0) { |
| 90 | + b |= *nbname++ - 'A'; // pridame nizsi nibble |
| 91 | + c++; // opet spocitame pocet odebranych znaku |
| 92 | + } |
| 93 | + *name++ = b; // ulozime znak do vysledku a posuneme ukazatel |
| 94 | + } |
| 95 | + *name = 0x0; // ulozime ukoncovaci 0 |
| 96 | +} |
| 97 | + |
| 98 | +/** Prevod zadaneho textu do NETBIOS kodovani |
| 99 | + * \param name Ukazatel na prevadene jmeno. |
| 100 | + * \param nbname Ukazatel na misto, kam vytvarime jmeno. |
| 101 | + * \param outlen Pocet vystupnich znaku (mimo ukoncovaci 0) musi byt delitelne 2 |
| 102 | + */ |
| 103 | +void ESP8266NetBIOS::_makenbname(char *name, char *nbname, uint8_t outlen) |
| 104 | +{ |
| 105 | + uint8_t b; |
| 106 | + uint8_t c = 0; |
| 107 | + |
| 108 | + while (c < (outlen - 2)) { |
| 109 | + b = *name; // prevadeny znak |
| 110 | + if (b) { |
| 111 | + name++; // zatim se posunujeme |
| 112 | + } else { |
| 113 | + b = 0x20; // konec retezce je nahrazeny mezerou |
| 114 | + } |
| 115 | + *nbname++ = (b >> 4) + 'A'; // jeden nibble ze znaku |
| 116 | + *nbname++ = (b & 0xf) + 'A'; // druhy nibble ze znaku |
| 117 | + c += 2; // pocet prevedenych znaku |
| 118 | + } |
| 119 | + *nbname++ = 'A'; |
| 120 | + *nbname++ = 'A'; // ulozime ukoncovaci 0 v NBNS kodovani |
| 121 | + *nbname = 0; // ulozime ukoncovaci 0 retezce |
| 122 | +} |
| 123 | + |
| 124 | +ESP8266NetBIOS::ESP8266NetBIOS():_pcb(NULL) |
| 125 | +{ |
| 126 | + |
| 127 | +} |
| 128 | +ESP8266NetBIOS::~ESP8266NetBIOS() |
| 129 | +{ |
| 130 | + end(); |
| 131 | +} |
| 132 | + |
| 133 | +// Vytvoreni a otevreni UDP soketu, pokud jeste neni... |
| 134 | +bool ESP8266NetBIOS::begin(const char *name) |
| 135 | +{ |
| 136 | + size_t n = strlen(name); |
| 137 | + if (n > sizeof(_name)) { |
| 138 | + // prilis dlouhe jmeno |
| 139 | + return false; |
| 140 | + } |
| 141 | + |
| 142 | + // presuneme jmeno zarizeni se soucasnou upravou na UPPER case |
| 143 | + for (int i = 0; i < n; ++i) { |
| 144 | + _name[i] = toupper(name[i]); |
| 145 | + } |
| 146 | + _name[n] = '\0'; |
| 147 | + |
| 148 | + if(_pcb != NULL) { |
| 149 | + return true; |
| 150 | + } |
| 151 | + ip_addr_t addr; |
| 152 | + addr.addr = INADDR_ANY; |
| 153 | + _pcb = udp_new(); |
| 154 | + udp_recv(_pcb, &_s_recv, (void *) this); |
| 155 | + err_t err = udp_bind(_pcb, &addr, NBNS_PORT); |
| 156 | + if(err != ERR_OK) { |
| 157 | + end(); |
| 158 | + return false; |
| 159 | + } |
| 160 | + return true; |
| 161 | +} |
| 162 | + |
| 163 | +void ESP8266NetBIOS::end() |
| 164 | +{ |
| 165 | + if(_pcb != NULL) { |
| 166 | + udp_remove(_pcb); |
| 167 | + _pcb = NULL; |
| 168 | + } |
| 169 | +} |
| 170 | + |
| 171 | +void ESP8266NetBIOS::_recv(udp_pcb *upcb, pbuf *pb, ip_addr_t *addr, u16_t port) |
| 172 | +{ |
| 173 | + while(pb != NULL) { |
| 174 | + uint8_t * data = (uint8_t*)((pb)->payload); |
| 175 | + size_t len = pb->len; |
| 176 | + ip_hdr* iphdr = reinterpret_cast<ip_hdr*>(data - UDP_HLEN - IP_HLEN); |
| 177 | + ip_addr_t saddr; |
| 178 | + saddr.addr = iphdr->src.addr; |
| 179 | + |
| 180 | + if (len >= sizeof(struct NBNSQUESTION)) { |
| 181 | + struct NBNSQUESTION * question = (struct NBNSQUESTION *)data; |
| 182 | + if (0 == (question->NBNSQ_FLAGS1 & 0x80)) { |
| 183 | + char name[ NBNS_MAX_HOSTNAME_LEN + 1 ]; // dekodovane dotazovane jmeno |
| 184 | + char *str; // pomocna promenna, pouze pro praci s retezcem |
| 185 | + |
| 186 | + _getnbname(&question->NBNSQ_NAME[0], (char *)&name, question->NBNSQ_NAMESIZE); // prevedeme dotazovane jmeno |
| 187 | + if ((str = strchr(name, ' ')) != NULL) { // jmeno hledaneho zarizeni v tomto pripade ukoncuje i mezera |
| 188 | + *str = '\0'; // ukoncime retezec na vyskytu prvni mezery |
| 189 | + } |
| 190 | + |
| 191 | + if (0 == strcmp(name, _name)) { |
| 192 | + // dotaz primo na nas |
| 193 | + struct NBNSANSWER nbnsa; // buffer, do ktereho je sestavena odpoved na dotaz |
| 194 | + |
| 195 | + nbnsa.NBNSA_ID = question->NBNSQ_ID;// ID dotazu kopirujeme do ID odpovedi |
| 196 | + nbnsa.NBNSA_FLAGS1 = 0x85; // priznak odpovedi |
| 197 | + nbnsa.NBNSA_FLAGS2 = 0; // vlajky 2 a response code |
| 198 | + nbnsa.NBNSA_QUESTIONCOUNT = LWIP_PLATFORM_HTONS(0); |
| 199 | + nbnsa.NBNSA_ANSWERCOUNT = LWIP_PLATFORM_HTONS(1);// poradove cislo odpovedi |
| 200 | + nbnsa.NBNSA_AUTHORITYCOUNT = LWIP_PLATFORM_HTONS(0); |
| 201 | + nbnsa.NBNSA_ADDITIONALRECORDCOUNT = LWIP_PLATFORM_HTONS(0); |
| 202 | + nbnsa.NBNSA_NAMESIZE = sizeof(nbnsa.NBNSA_NAME) - 1; // prekopirujeme delku jmena stanice |
| 203 | + _makenbname(_name, &nbnsa.NBNSA_NAME[0], sizeof(nbnsa.NBNSA_NAME) - 1); // prevedeme jmeno |
| 204 | + nbnsa.NBNSA_TYPE = LWIP_PLATFORM_HTONS(0x20); // NetBIOS name |
| 205 | + nbnsa.NBNSA_CLASS = LWIP_PLATFORM_HTONS(1); // Internet name |
| 206 | + nbnsa.NBNSA_TIMETOLIVE = LWIP_PLATFORM_HTONL(300000UL);// Time to live (30000 sekund) |
| 207 | + nbnsa.NBNSA_LENGTH = LWIP_PLATFORM_HTONS(6); |
| 208 | + nbnsa.NBNSA_NODEFLAGS = LWIP_PLATFORM_HTONS(0); |
| 209 | + nbnsa.NBNSA_NODEADDRESS = WiFi.localIP(); // ulozime nasi IP adresu |
| 210 | + |
| 211 | + pbuf* pbt = pbuf_alloc(PBUF_TRANSPORT, sizeof(nbnsa), PBUF_RAM); |
| 212 | + if(pbt != NULL) { |
| 213 | + uint8_t* dst = reinterpret_cast<uint8_t*>(pbt->payload); |
| 214 | + memcpy(dst, (uint8_t *)&nbnsa, sizeof(nbnsa)); |
| 215 | + udp_sendto(_pcb, pbt, &saddr, NBNS_PORT); |
| 216 | + pbuf_free(pbt); |
| 217 | + } |
| 218 | + } else if (0 == strcmp(name, "*")) { |
| 219 | + // obecny dotaz - mireny nejspis na nasi IP adresu |
| 220 | + struct NBNSANSWERN nbnsan; // buffer, do ktereho je sestavena odpoved na dotaz |
| 221 | + |
| 222 | + nbnsan.NBNSAN_ID = question->NBNSQ_ID;// ID dotazu kopirujeme do ID odpovedi |
| 223 | + nbnsan.NBNSAN_FLAGS1 = 0x84; // priznak odpovedi |
| 224 | + nbnsan.NBNSAN_FLAGS2 = 0; // vlajky 2 a response code |
| 225 | + nbnsan.NBNSAN_QUESTIONCOUNT = LWIP_PLATFORM_HTONS(0); |
| 226 | + nbnsan.NBNSAN_ANSWERCOUNT = LWIP_PLATFORM_HTONS(1);// poradove cislo odpovedi |
| 227 | + nbnsan.NBNSAN_AUTHORITYCOUNT = LWIP_PLATFORM_HTONS(0); |
| 228 | + nbnsan.NBNSAN_ADDITIONALRECORDCOUNT = LWIP_PLATFORM_HTONS(0); |
| 229 | + nbnsan.NBNSAN_NAMESIZE = question->NBNSQ_NAMESIZE; // prekopirujeme delku jmena stanice |
| 230 | + memcpy(nbnsan.NBNSAN_NAME, question->NBNSQ_NAME, sizeof(nbnsan.NBNSAN_NAME)); // prekopirujeme dotazovane jmeno |
| 231 | + nbnsan.NBNSAN_TYPE = LWIP_PLATFORM_HTONS(0x21); // NBSTAT |
| 232 | + nbnsan.NBNSAN_CLASS = LWIP_PLATFORM_HTONS(1); // Internet name |
| 233 | + nbnsan.NBNSAN_TIMETOLIVE = LWIP_PLATFORM_HTONL(0); |
| 234 | + nbnsan.NBNSAN_LENGTH = LWIP_PLATFORM_HTONS(4 + sizeof(nbnsan.NBNSAN_NNAME)); |
| 235 | + nbnsan.NBNSAN_NUMBER = 1; // Number of names |
| 236 | + memset(nbnsan.NBNSAN_NNAME, 0x20, sizeof(nbnsan.NBNSAN_NNAME)); |
| 237 | + memcpy(nbnsan.NBNSAN_NNAME, _name, strlen(_name)); |
| 238 | + nbnsan.NBNSAN_NTYPE = 0; // Workstation/Redirector |
| 239 | + nbnsan.NBNSAN_NFLAGS = LWIP_PLATFORM_HTONS(0x400); // b-node, unique, active |
| 240 | + |
| 241 | + pbuf* pbt = pbuf_alloc(PBUF_TRANSPORT, sizeof(nbnsan), PBUF_RAM); |
| 242 | + if(pbt != NULL) { |
| 243 | + uint8_t* dst = reinterpret_cast<uint8_t*>(pbt->payload); |
| 244 | + memcpy(dst, (uint8_t *)&nbnsan, sizeof(nbnsan)); |
| 245 | + udp_sendto(_pcb, pbt, &saddr, NBNS_PORT); |
| 246 | + pbuf_free(pbt); |
| 247 | + } |
| 248 | + } |
| 249 | + } |
| 250 | + } |
| 251 | + |
| 252 | + pbuf * this_pb = pb; |
| 253 | + pb = pb->next; |
| 254 | + this_pb->next = NULL; |
| 255 | + pbuf_free(this_pb); |
| 256 | + } |
| 257 | +} |
| 258 | + |
| 259 | +void ESP8266NetBIOS::_s_recv(void *arg, udp_pcb *upcb, pbuf *p, struct ip_addr *addr, uint16_t port) |
| 260 | +{ |
| 261 | + reinterpret_cast<ESP8266NetBIOS*>(arg)->_recv(upcb, p, addr, port); |
| 262 | +} |
| 263 | + |
| 264 | +ESP8266NetBIOS NBNS; |
| 265 | +// EOF |
0 commit comments