/**************************************************************************** Function: int ChipKITUDPAvailable(UDP_SOCKET hUDP) Description: Returnes the number of available (unread) bytes in the UDP cache Parameters: hUDP - the UDP socket to use Returns: The number of available byte to ready to read Remarks: ***************************************************************************/ int ChipKITUDPAvailable(UDP_SOCKET hUDP) { // run the tasks so we read into the buffer ChipKITPeriodicTasks(); // return what we got. return(UDPAvailable(hUDP)); }
/**************************************************************************** Function: int ChipKITUDPReadPacket(UDP_SOCKET hUDP, BYTE * rgbBuff, WORD cbBuff, NODE_INFO * pnodeInfo, WORD * pwPort) Description: reads the available bytes out of the UDP cache Parameters: hUDP - the UDP socket to use rgbBuff - a pointer to a byte buffer to receive the bytes cbBuff - the maximum size of the receive buffer pnodeInfo - a pointer to a NODE_INFO structure with the remote IP and MAC address that the data came from pwPort - a pointer to the a WORD to receive the remote port that the data came from Returns: The number of bytes actually read into the buffer. 0 if no bytes were available to read. The remote IP/MAC/Port is the last know remote endpoint that sent data and may not represent the endpoint for all of the data read as the data may have come in from several different endpoints; only the last endpoint is preserved. If 0 is returned the endpoint data is the last valid endpoint to send data to the socket. In general, if 0 is returned the endpoint data is considered useless. Remarks: ***************************************************************************/ int ChipKITUDPReadPacket(UDP_SOCKET hUDP, BYTE * rgbBuff, WORD cbBuff, NODE_INFO * pnodeInfo, WORD * pwPort) { WORD cbAvailable = UDPAvailable(hUDP); WORD cbRead = UDPRead(hUDP, rgbBuff, cbBuff); UDPSB * pUDPSB = UDPGetUDPSB(hUDP); if(pnodeInfo != NULL) { if(pUDPSB != NULL) { *pnodeInfo = pUDPSB->remoteNodeInfo; } else { memset(pnodeInfo, 0, sizeof(NODE_INFO)); } } if(pwPort != NULL) { if(pUDPSB != NULL) { *pwPort = pUDPSB->remotePort; } else { *pwPort = INVALID_UDP_PORT; } } ChipKITPeriodicTasks(); if(cbRead == cbAvailable) { return(cbRead); } else { return(-1*cbAvailable); } }
// Notes on dnsNSMax and cDhcpNS. You would think that we should only cycle through cDhcpNS as this is the number // DNS servers given to us by DHCP, however, sometimes DHCP does not give us good DNS servers and for the // SNTP server to work, we need a good DNS server. So we continue to check the default, pre initialized google DNS // servers after the DHCP DNS servers are checked first. If we get a lot of DNS servers from DHCP, then ultimately // all of the pre installed google servers will be overwritten in the dnsNSMax list, but if we got a lot of DNS servers // the assumption is that they are good ones. In particular, I found that Verizon did not give me very good DNS servers, they // could not resolve the SNTP time servers. static void DNSStateMachine(const LLADP * pLLAdp) { IPSTATUS status = ipsSuccess; if(pLLAdp == NULL || pLLAdp->pDNSMem == NULL || !ILIsIPSetup(pLLAdp, NULL)) { return; } switch(pLLAdp->pDNSMem->dnsState) { case dnsSend: if(pLLAdp->pDNSMem->dnsNSMax == 0) { pLLAdp->pDNSMem->dnsState = dnsReady; pLLAdp->pDNSMem->cTry = 0; break; } else if(pLLAdp->pDNSMem->cTry >= (pLLAdp->pDNSMem->dnsNSMax * DNSMINTRY)) { if(pLLAdp->pDNSMem->iDNSCur == pLLAdp->pDNSMem->iDNSWorks) { pLLAdp->pDNSMem->iDNSWorks = DNSiInvalid; } pLLAdp->pDNSMem->iDNSCur = DNSiInvalid; pLLAdp->pDNSMem->dnsState = dnsReady; pLLAdp->pDNSMem->cTry = 0; break; } else if(pLLAdp->pDNSMem->iDNSWorks < pLLAdp->pDNSMem->dnsNSMax) { pLLAdp->pDNSMem->iDNSCur = pLLAdp->pDNSMem->iDNSWorks; } else if(pLLAdp->pDNSMem->iDNSCur >= pLLAdp->pDNSMem->dnsNSMax) { pLLAdp->pDNSMem->iDNSCur = 0; } else { pLLAdp->pDNSMem->iDNSCur = (pLLAdp->pDNSMem->iDNSCur + 1) % pLLAdp->pDNSMem->dnsNSMax; } // set this up for an attempt to get the IP address memcpy(&pLLAdp->pDNSMem->socket.s.ipRemote, &pLLAdp->pDNSMem->dnsNS[pLLAdp->pDNSMem->iDNSCur], ILIPSize(pLLAdp)); // fall thru on success case dnsReadySend: // clear the current IP address memset(&pLLAdp->pDNSMem->ip, 0, sizeof(IPv4or6)); // just make this unique for this pass pLLAdp->pDNSMem->dnsDG.dnsHdr.ID++; // set my timers and counts pLLAdp->pDNSMem->cTry++; pLLAdp->pDNSMem->tTimeout = dnsWaitForRetry; pLLAdp->pDNSMem->tStart = SYSGetMilliSecond(); // send out the DNS datagram UDPSend(&pLLAdp->pDNSMem->socket, (uint8_t *) &pLLAdp->pDNSMem->dnsDG, pLLAdp->pDNSMem->cbDNSDG, &status); // see if it went out. if(IsIPStatusAnError(status)) { pLLAdp->pDNSMem->dnsState = dnsReady; pLLAdp->pDNSMem->cTry = 0; } else { pLLAdp->pDNSMem->dnsState = dnsWaiting; } break; case dnsWaiting: { uint16_t cbDG = UDPAvailable(&pLLAdp->pDNSMem->socket) ; IPSTATUS status; if(cbDG > sizeof(DNSHDR)) { // we need some space for the datagram uint8_t rgbDNSDG[DNSMAXUDPSIZE]; DNSDG * pDNSDG = (DNSDG *) rgbDNSDG; uint8_t * pEnd = NULL; uint8_t * pCName = NULL; DNSRR * pDNSRRA = NULL; // read the DNS datagram cbDG = UDPRead(&pLLAdp->pDNSMem->socket, rgbDNSDG, DNSMAXUDPSIZE, &status); // There are some servers setting this and they should not! RFC 6195 2.1 and I need to ignore it // Plus this is used internally to determine if we are in machine or network order. This is in network order right now. pDNSDG->dnsHdr.Z = 0; // now put in machine order. cbDG = ExDNSDG(pDNSDG, cbDG); pEnd = rgbDNSDG + cbDG; // if this is not a my response, keep waiting // remember the ID is not exchanged, so network and machine order will have the same ID if(!pDNSDG->dnsHdr.QR || pDNSDG->dnsHdr.ID != pLLAdp->pDNSMem->dnsDG.dnsHdr.ID) { break; } // ops and error occured, jump to finish with error // go to the error state with no address found if(pDNSDG->dnsHdr.RCODE != DNSRCODENoError) { // this is a failure case; go to the next DNS server pLLAdp->pDNSMem->dnsState = dnsSend; break; } // get the canonical name for what we are looking for // the first record is the question which has the name we used. // we need to use the one in this datagram because we do memory range // checking and if we use the QR we sent, it would be out of the memory range pCName = DNSFindCName(pDNSDG, DNSRRAN, pDNSDG->rrRecords, pEnd); // now find the A record pDNSRRA = DNSFindRR(pDNSDG, NULL, DNSRRAN, DNSTYPEA, DNSCLASSIN, pCName, pEnd); // if we got the A record we are done if( pDNSRRA != NULL && ((ILIsIPv6(pLLAdp) && pDNSRRA->RDLENGTH == sizeof(IPv6)) || (!ILIsIPv6(pLLAdp) && pDNSRRA->RDLENGTH == sizeof(IPv4)) ) ) { // copy in our result memcpy(&pLLAdp->pDNSMem->ip, pDNSRRA->RDATA, pDNSRRA->RDLENGTH); // say we are done pLLAdp->pDNSMem->dnsState = dnsReady; pLLAdp->pDNSMem->cTry = 0; // We got an IP so we know this DNS server can work, remember that if(pLLAdp->pDNSMem->iDNSCur < pLLAdp->pDNSMem->dnsNSMax) { pLLAdp->pDNSMem->iDNSWorks = pLLAdp->pDNSMem->iDNSCur; } // get out we found it and are done. break; } // by default we will go to the next well know sever unless we pick up a better NS to go to pLLAdp->pDNSMem->dnsState = dnsSend; // we did not find anything, so update our DNS request to use the new CName if(pCName != pDNSDG->rrRecords) { // make the new DNS packet with the CName instead of what we were using uint16_t cbMax = DNSCreateIPv4QueryDN(pLLAdp, rgbDNSDG, pCName, pEnd, &pLLAdp->pDNSMem->dnsDG); pLLAdp->pDNSMem->cbDNSDG = ExDNSDG(&pLLAdp->pDNSMem->dnsDG, cbMax); } // Didn't find an A record // But maybe there is a name server we should try. // If we asked for recursion and go it, no sense working the issue ourselves. // likewise if there are now NS with IP addresses, no sense working the issue if(!(pDNSDG->dnsHdr.RD && pDNSDG->dnsHdr.RA) && pDNSDG->dnsHdr.NSCOUNT > 0 && pDNSDG->dnsHdr.ARCOUNT > 0) { // this DNS server gave us something, so remember it if(pLLAdp->pDNSMem->iDNSCur < pLLAdp->pDNSMem->dnsNSMax) { pLLAdp->pDNSMem->iDNSWorks = pLLAdp->pDNSMem->iDNSCur; } pDNSRRA = DNSFindNSARR(pDNSDG, pCName, pEnd, DNSTYPEA); // if this looks like a good IP address to try, lets contact that name server if(pDNSRRA != NULL && pDNSRRA->RDLENGTH == sizeof(IPv4)) { // say were are not using one of our well known DNS servers pLLAdp->pDNSMem->iDNSCur = DNSiInvalid; // Put the IP in our socket memcpy(&pLLAdp->pDNSMem->socket.s.ipRemote, pDNSRRA->RDATA, pDNSRRA->RDLENGTH); pLLAdp->pDNSMem->dnsState = dnsReadySend; } } else if(pLLAdp->pDNSMem->iDNSWorks == pLLAdp->pDNSMem->iDNSCur) { pLLAdp->pDNSMem->iDNSWorks = DNSiInvalid; } } else if((SYSGetMilliSecond() - pLLAdp->pDNSMem->tStart) >= pLLAdp->pDNSMem->tTimeout) { pLLAdp->pDNSMem->dnsState = dnsWaitTry; } } break; case dnsWaitTry: if(pLLAdp->pDNSMem->iDNSWorks == pLLAdp->pDNSMem->iDNSCur) { pLLAdp->pDNSMem->iDNSWorks = DNSiInvalid; } if((pLLAdp->pDNSMem->cTry % DNSMINTRY) == 0) { pLLAdp->pDNSMem->dnsState = dnsSend; } else { pLLAdp->pDNSMem->dnsState = dnsReadySend; } break; case dnsReady: pLLAdp->pDNSMem->cTry = 0; break; // noting to do here, either we have something in the DNS memory IP or not. case dnsRedirect: case dnsUninit: default: break; } }