xref: /FreeRTOS-Plus-TCP-v4.0.0/source/FreeRTOS_IPv6_Sockets.c (revision b23fa86ac476770d3224c07213bec32f5b1628bd)
1 /*
2  * FreeRTOS+TCP <DEVELOPMENT BRANCH>
3  * Copyright (C) 2022 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
4  *
5  * SPDX-License-Identifier: MIT
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy of
8  * this software and associated documentation files (the "Software"), to deal in
9  * the Software without restriction, including without limitation the rights to
10  * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11  * the Software, and to permit persons to whom the Software is furnished to do so,
12  * subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in all
15  * copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19  * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23  *
24  * http://aws.amazon.com/freertos
25  * http://www.FreeRTOS.org
26  */
27 
28 /**
29  * @file FreeRTOS_IPv6_Sockets.c
30  * @brief Implements the Sockets API based on Berkeley sockets for the FreeRTOS+TCP network stack.
31  *        Sockets are used by the application processes to interact with the IP-task which in turn
32  *        interacts with the hardware.
33  */
34 
35 /* Standard includes. */
36 #include <stdint.h>
37 #include <stdio.h>
38 
39 /* FreeRTOS includes. */
40 #include "FreeRTOS.h"
41 
42 /* FreeRTOS+TCP includes. */
43 #include "FreeRTOS_IP.h"
44 #include "FreeRTOS_IPv6_Sockets.h"
45 
46 /* *INDENT-OFF* */
47 #if( ipconfigUSE_IPv6 != 0 )
48 /* *INDENT-ON* */
49 
50 #if ( ipconfigUSE_TCP == 1 )
51 
52 /**
53  * @brief Called by pxTCPSocketLookup(), this function will check if a socket
54  *        is connected to a remote IP-address. It will be called from a loop
55  *        iterating through all sockets.
56  * @param[in] pxSocket The socket to be inspected.
57  * @param[in] pxAddress The IPv4/IPv6 address.
58  * @return The socket in case it is connected to the remote IP-address.
59  */
pxTCPSocketLookup_IPv6(FreeRTOS_Socket_t * pxSocket,const IPv46_Address_t * pxAddress)60     FreeRTOS_Socket_t * pxTCPSocketLookup_IPv6( FreeRTOS_Socket_t * pxSocket,
61                                                 const IPv46_Address_t * pxAddress )
62     {
63         FreeRTOS_Socket_t * pxResult = NULL;
64 
65         if( ( pxSocket != NULL ) && ( pxAddress != NULL ) )
66         {
67             if( pxSocket->bits.bIsIPv6 != pdFALSE_UNSIGNED )
68             {
69                 if( pxAddress->xIs_IPv6 != pdFALSE )
70                 {
71                     if( memcmp( pxSocket->u.xTCP.xRemoteIP.xIP_IPv6.ucBytes, pxAddress->xIPAddress.xIP_IPv6.ucBytes, ipSIZE_OF_IPv6_ADDRESS ) == 0 )
72                     {
73                         /* For sockets not in listening mode, find a match with
74                          * uxLocalPort, ulRemoteIP AND uxRemotePort. */
75                         pxResult = pxSocket;
76                     }
77                 }
78             }
79             else
80             {
81                 if( pxAddress->xIs_IPv6 == pdFALSE )
82                 {
83                     if( pxSocket->u.xTCP.xRemoteIP.ulIP_IPv4 == pxAddress->xIPAddress.ulIP_IPv4 )
84                     {
85                         /* For sockets not in listening mode, find a match with
86                          * uxLocalPort, ulRemoteIP AND uxRemotePort. */
87                         pxResult = pxSocket;
88                     }
89                 }
90             }
91         }
92 
93         return pxResult;
94     }
95 
96 #endif /* if ( ( ipconfigUSE_TCP == 1 ) */
97 
98 /**
99  * @brief Called by prvSendUDPPacket(), this function will UDP packet
100  *        fields and IPv6 address for the packet to be send.
101  * @param[in] pxNetworkBuffer  The packet to be sent.
102  * @param[in] pxDestinationAddress The IPv4 socket address.
103  * @return  Returns NULL, always.
104  */
xSend_UDP_Update_IPv6(NetworkBufferDescriptor_t * pxNetworkBuffer,const struct freertos_sockaddr * pxDestinationAddress)105 void * xSend_UDP_Update_IPv6( NetworkBufferDescriptor_t * pxNetworkBuffer,
106                               const struct freertos_sockaddr * pxDestinationAddress )
107 {
108     /* MISRA Ref 11.3.1 [Misaligned access] */
109     /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
110     /* coverity[misra_c_2012_rule_11_3_violation] */
111     UDPPacket_IPv6_t * pxUDPPacket_IPv6 = ( ( UDPPacket_IPv6_t * ) pxNetworkBuffer->pucEthernetBuffer );
112 
113     pxNetworkBuffer->xIPAddress.ulIP_IPv4 = 0U;
114 
115     configASSERT( pxDestinationAddress != NULL );
116     ( void ) memcpy( pxUDPPacket_IPv6->xIPHeader.xDestinationAddress.ucBytes, pxDestinationAddress->sin_address.xIP_IPv6.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
117     ( void ) memcpy( pxNetworkBuffer->xIPAddress.xIP_IPv6.ucBytes, pxDestinationAddress->sin_address.xIP_IPv6.ucBytes, ipSIZE_OF_IPv6_ADDRESS );
118     pxUDPPacket_IPv6->xEthernetHeader.usFrameType = ipIPv6_FRAME_TYPE;
119 
120     return NULL;
121 }
122 
123 /**
124  * @brief Called by FreeRTOS_recvfrom(), this function will update socket
125  *        address with IPv6 address from the packet received.
126  * @param[in] pxNetworkBuffer  The packet received.
127  * @param[in] pxSourceAddress The IPv4 socket address.
128  * @return The Payload Offset.
129  */
xRecv_Update_IPv6(const NetworkBufferDescriptor_t * pxNetworkBuffer,struct freertos_sockaddr * pxSourceAddress)130 size_t xRecv_Update_IPv6( const NetworkBufferDescriptor_t * pxNetworkBuffer,
131                           struct freertos_sockaddr * pxSourceAddress )
132 {
133     /* MISRA Ref 11.3.1 [Misaligned access] */
134     /* More details at: https://github.com/FreeRTOS/FreeRTOS-Plus-TCP/blob/main/MISRA.md#rule-113 */
135     /* coverity[misra_c_2012_rule_11_3_violation] */
136     const UDPPacket_IPv6_t * pxUDPPacketV6 = ( ( const UDPPacket_IPv6_t * ) pxNetworkBuffer->pucEthernetBuffer );
137     size_t uxPayloadOffset = 0;
138 
139     if( pxUDPPacketV6->xEthernetHeader.usFrameType == ipIPv6_FRAME_TYPE )
140     {
141         if( pxSourceAddress != NULL )
142         {
143             ( void ) memcpy( ( void * ) pxSourceAddress->sin_address.xIP_IPv6.ucBytes,
144                              ( const void * ) pxUDPPacketV6->xIPHeader.xSourceAddress.ucBytes,
145                              ipSIZE_OF_IPv6_ADDRESS );
146             pxSourceAddress->sin_family = ( uint8_t ) FREERTOS_AF_INET6;
147             pxSourceAddress->sin_port = pxNetworkBuffer->usPort;
148         }
149 
150         uxPayloadOffset = ipUDP_PAYLOAD_OFFSET_IPv6;
151     }
152 
153     return uxPayloadOffset;
154 }
155 
156 
157 /**
158  * @brief Converts a 4 bit (nibble) value to a readable hex character, e.g. 14 becomes 'e'.
159  * @param usValue  The value to be converted, must be between 0 and 15.
160  * @return The character, between '0' and '9', or between 'a' and 'f'.
161  */
cHexToChar(uint16_t usValue)162 char cHexToChar( uint16_t usValue )
163 {
164     char cReturn = '0';
165 
166     if( usValue <= 9U )
167     {
168         cReturn = ( char ) ( cReturn + usValue );
169     }
170     else if( usValue <= 15U )
171     {
172         cReturn = 'a';
173         cReturn = ( char ) ( cReturn + ( usValue - ( uint16_t ) 10 ) );
174     }
175     else
176     {
177         /* The value passed to 'usValue' has been and-ed with 0x0f,
178          * so this else clause should never be reached. */
179         configASSERT( 0 == 1 );
180     }
181 
182     return cReturn;
183 }
184 
185 /*-----------------------------------------------------------*/
186 
187 /**
188  * @brief Convert a short numeric value to a hex string of at most 4 characters.
189  *        The resulting string is **not** null-terminated. The resulting string
190  *        will not have leading zero's, except when 'usValue' equals zero.
191  * @param[in] pcBuffer  The buffer to which the string is written.
192  * @param[in] uxBufferSize  The size of the buffer pointed to by 'pcBuffer'.
193  * @param[in] usValue  The 16-bit value to be converted.
194  * @return The number of bytes written to 'pcBuffer'.
195  */
uxHexPrintShort(char * pcBuffer,size_t uxBufferSize,uint16_t usValue)196 socklen_t uxHexPrintShort( char * pcBuffer,
197                            size_t uxBufferSize,
198                            uint16_t usValue )
199 {
200     const size_t uxNibbleCount = 4U;
201     size_t uxNibble;
202     socklen_t uxIndex = 0U;
203     uint16_t usShifter = usValue;
204     BaseType_t xHadNonZero = pdFALSE;
205 
206     for( uxNibble = 0; uxNibble < uxNibbleCount; uxNibble++ )
207     {
208         uint16_t usNibble = ( usShifter >> 12 ) & 0x0FU;
209 
210         if( usNibble != 0U )
211         {
212             xHadNonZero = pdTRUE;
213         }
214 
215         if( ( xHadNonZero != pdFALSE ) || ( uxNibble == ( uxNibbleCount - 1U ) ) )
216         {
217             if( uxIndex >= ( uxBufferSize - 1U ) )
218             {
219                 break;
220             }
221 
222             pcBuffer[ uxIndex ] = cHexToChar( usNibble );
223             uxIndex++;
224         }
225 
226         usShifter = ( uint16_t ) ( usShifter << 4 );
227     }
228 
229     return uxIndex;
230 }
231 /*-----------------------------------------------------------*/
232 
233 /**
234  * @brief Scan the binary IPv6 address and find the longest train of consecutive zero's.
235  *        The result of this search will be stored in 'xZeroStart' and 'xZeroLength'.
236  * @param pxSet the set of parameters as used by FreeRTOS_inet_ntop6().
237  */
prv_ntop6_search_zeros(struct sNTOP6_Set * pxSet)238 void prv_ntop6_search_zeros( struct sNTOP6_Set * pxSet )
239 {
240     BaseType_t xIndex = 0;            /* The index in the IPv6 address: 0..7. */
241     BaseType_t xCurStart = 0;         /* The position of the first zero found so far. */
242     BaseType_t xCurLength = 0;        /* The number of zero's seen so far. */
243     const BaseType_t xShortCount = 8; /* An IPv6 address consists of 8 shorts. */
244 
245     /* Default: when xZeroStart is negative, it won't match with any xIndex. */
246     pxSet->xZeroStart = -1;
247 
248     /* Look for the longest train of zero's 0:0:0:... */
249     for( ; xIndex < xShortCount; xIndex++ )
250     {
251         uint16_t usValue = pxSet->pusAddress[ xIndex ];
252 
253         if( usValue == 0U )
254         {
255             if( xCurLength == 0 )
256             {
257                 /* Remember the position of the first zero. */
258                 xCurStart = xIndex;
259             }
260 
261             /* Count consecutive zeros. */
262             xCurLength++;
263         }
264 
265         if( ( usValue != 0U ) || ( xIndex == ( xShortCount - 1 ) ) )
266         {
267             /* Has a longer train of zero's been found? */
268             if( ( xCurLength > 1 ) && ( pxSet->xZeroLength < xCurLength ) )
269             {
270                 /* Remember the number of consecutive zeros. */
271                 pxSet->xZeroLength = xCurLength;
272                 /* Remember the index of the first zero found. */
273                 pxSet->xZeroStart = xCurStart;
274             }
275 
276             /* Reset the counter of consecutive zeros. */
277             xCurLength = 0;
278         }
279     }
280 }
281 /*-----------------------------------------------------------*/
282 
283 /**
284  * @brief The location is now at the longest train of zero's. Two colons have to
285  *        be printed without a numeric value, e.g. "ff02::1".
286  * @param pcDestination the output buffer where the colons will be printed.
287  * @param uxSize the remaining length of the output buffer.
288  * @param pxSet the set of parameters as used by FreeRTOS_inet_ntop6().
289  * @return pdPASS in case the output buffer is big enough to contain the colons.
290  * @note uxSize must be at least 2, enough to print "::". The string will get
291  *       null-terminated later on.
292  */
prv_ntop6_write_zeros(char * pcDestination,size_t uxSize,struct sNTOP6_Set * pxSet)293 static BaseType_t prv_ntop6_write_zeros( char * pcDestination,
294                                          size_t uxSize,
295                                          struct sNTOP6_Set * pxSet )
296 {
297     BaseType_t xReturn = pdPASS;
298     const BaseType_t xShortCount = 8; /* An IPv6 address consists of 8 shorts. */
299 
300     if( pxSet->uxTargetIndex <= ( uxSize - 1U ) )
301     {
302         pcDestination[ pxSet->uxTargetIndex ] = ':';
303         pxSet->uxTargetIndex++;
304 
305         if( ( pxSet->xIndex + pxSet->xZeroLength ) == xShortCount )
306         {
307             /* Reached the last index, write a second ":". */
308             if( pxSet->uxTargetIndex <= ( uxSize - 1U ) )
309             {
310                 pcDestination[ pxSet->uxTargetIndex ] = ':';
311                 pxSet->uxTargetIndex++;
312             }
313             else
314             {
315                 /* Can not write the second colon. */
316                 xReturn = pdFAIL;
317             }
318         }
319         else
320         {
321             /* Otherwise the function prv_ntop6_write_short() will places the second colon. */
322         }
323     }
324     else
325     {
326         /* Can not write the first colon. */
327         xReturn = pdFAIL;
328     }
329 
330     return xReturn;
331 }
332 /*-----------------------------------------------------------*/
333 
334 /**
335  * @brief Write a short value, as a hex number with at most 4 characters. E.g. the
336  *        value 15 will be printed as "f".
337  * @param pcDestination the output buffer where the hex number is to be printed.
338  * @param uxSize the remaining length of the output buffer.
339  * @param pxSet the set of parameters as used by FreeRTOS_inet_ntop6().
340  * @return pdPASS in case the output buffer is big enough to contain the string.
341  * @note uxSize must be at least 4, enough to print "abcd". The string will get
342  *       null-terminated later on.
343  */
prv_ntop6_write_short(char * pcDestination,size_t uxSize,struct sNTOP6_Set * pxSet)344 static BaseType_t prv_ntop6_write_short( char * pcDestination,
345                                          size_t uxSize,
346                                          struct sNTOP6_Set * pxSet )
347 {
348     socklen_t uxLength;
349     BaseType_t xReturn = pdPASS;
350     const size_t uxBytesPerShortValue = 4U;
351 
352     if( pxSet->xIndex > 0 )
353     {
354         if( pxSet->uxTargetIndex >= ( uxSize - 1U ) )
355         {
356             xReturn = pdFAIL;
357         }
358         else
359         {
360             pcDestination[ pxSet->uxTargetIndex ] = ':';
361             pxSet->uxTargetIndex++;
362         }
363     }
364 
365     if( xReturn == pdPASS )
366     {
367         /* If there is enough space to write a short. */
368         if( pxSet->uxTargetIndex <= ( uxSize - uxBytesPerShortValue ) )
369         {
370             /* Write hex value of short. at most 4 + 1 bytes. */
371             uxLength = uxHexPrintShort( &( pcDestination[ pxSet->uxTargetIndex ] ),
372                                         uxBytesPerShortValue + 1U,
373                                         FreeRTOS_ntohs( pxSet->pusAddress[ pxSet->xIndex ] ) );
374 
375             /* uxLength will be non zero and positive always. */
376             pxSet->uxTargetIndex += uxLength;
377         }
378         else
379         {
380             xReturn = pdFAIL;
381         }
382     }
383 
384     return xReturn;
385 }
386 /*-----------------------------------------------------------*/
387 
388 /**
389  * @brief This function converts a binary IPv6 address to a human readable notation.
390  *
391  * @param[in] pvSource The binary address, 16 bytes long..
392  * @param[out] pcDestination The human-readable ( hexadecimal ) notation of the
393  *                            address.
394  * @param[in] uxSize The size of pvDestination. A value of 40 is recommended.
395  *
396  * @return pdPASS if the translation was successful or else pdFAIL.
397  */
FreeRTOS_inet_ntop6(const void * pvSource,char * pcDestination,socklen_t uxSize)398 const char * FreeRTOS_inet_ntop6( const void * pvSource,
399                                   char * pcDestination,
400                                   socklen_t uxSize )
401 {
402     const char * pcReturn;  /* The return value, which is either 'pcDestination' or NULL. */
403     struct sNTOP6_Set xSet; /* A set of values for easy exchange with the helper functions prv_ntop6_xxx(). */
404 
405     ( void ) memset( &( xSet ), 0, sizeof( xSet ) );
406 
407     xSet.pusAddress = pvSource;
408 
409     if( uxSize < 3U )
410     {
411         /* Can not even print :: */
412     }
413     else
414     {
415         prv_ntop6_search_zeros( &( xSet ) );
416 
417         while( xSet.xIndex < 8 )
418         {
419             if( xSet.xIndex == xSet.xZeroStart )
420             {
421                 if( prv_ntop6_write_zeros( pcDestination, uxSize, &( xSet ) ) == pdFAIL )
422                 {
423                     break;
424                 }
425 
426                 xSet.xIndex += xSet.xZeroLength;
427             }
428             else
429             {
430                 if( prv_ntop6_write_short( pcDestination, uxSize, &( xSet ) ) == pdFAIL )
431                 {
432                     break;
433                 }
434 
435                 xSet.xIndex++;
436             }
437         }
438     }
439 
440     if( xSet.xIndex < 8 )
441     {
442         /* Didn't reach the last nibble: clear the string. */
443         pcReturn = NULL;
444     }
445     else
446     {
447         pcDestination[ xSet.uxTargetIndex ] = '\0';
448         pcReturn = pcDestination;
449     }
450 
451     return pcReturn;
452 }
453 /*-----------------------------------------------------------*/
454 
455 /**
456  * @brief Converting a readable IPv6 address to its binary form, add one nibble.
457  *
458  * @param[in] pxSet  A set of variables describing the conversion.
459  * @param[in] ucNew  The hex value, between 0 and 15
460  * @param[in] ch  The character, such as '5', 'f', or ':'.
461  *
462  * @return pdTRUE when the nibble was added, otherwise pdFALSE.
463  */
prv_inet_pton6_add_nibble(struct sPTON6_Set * pxSet,uint8_t ucNew,char ch)464 static BaseType_t prv_inet_pton6_add_nibble( struct sPTON6_Set * pxSet,
465                                              uint8_t ucNew,
466                                              char ch )
467 {
468     BaseType_t xReturn = pdPASS;
469 
470     if( ucNew != ( uint8_t ) socketINVALID_HEX_CHAR )
471     {
472         /* Shift in 4 bits. */
473         pxSet->ulValue <<= 4;
474         pxSet->ulValue |= ( uint32_t ) ucNew;
475 
476         /* Remember that ulValue is valid now. */
477         pxSet->xHadDigit = pdTRUE;
478 
479         /* Check if the number is not becoming larger than 16 bits. */
480         if( pxSet->ulValue > 0xffffU )
481         {
482             /* The highest nibble has already been set,
483              * an overflow would occur.  Break out of the for-loop. */
484             xReturn = pdFAIL;
485         }
486     }
487     else if( ch == ':' )
488     {
489         if( pxSet->xHadDigit == pdFALSE )
490         {
491             /* A "::" sequence has been received. Check if it is not a third colon. */
492             if( pxSet->xColon >= 0 )
493             {
494                 xReturn = pdFAIL;
495             }
496             else
497             {
498                 /* Two or more zero's are expected, starting at position 'xColon'. */
499                 pxSet->xColon = pxSet->xTargetIndex;
500             }
501         }
502         else
503         {
504             if( pxSet->xTargetIndex <= pxSet->xHighestIndex )
505             {
506                 /* Store a short value at position 'xTargetIndex'. */
507                 pxSet->pucTarget[ pxSet->xTargetIndex ] = ( uint8_t ) ( ( pxSet->ulValue >> 8 ) & 0xffU );
508                 pxSet->pucTarget[ pxSet->xTargetIndex + 1 ] = ( uint8_t ) ( pxSet->ulValue & 0xffU );
509                 pxSet->xTargetIndex += 2;
510                 pxSet->xHadDigit = pdFALSE;
511                 pxSet->ulValue = 0U;
512             }
513             else
514             {
515                 xReturn = pdFAIL;
516             }
517         }
518     }
519     else
520     {
521         /* When an IPv4 address or rubbish is provided, this statement will be reached. */
522         xReturn = pdFAIL;
523     }
524 
525     return xReturn;
526 }
527 /*-----------------------------------------------------------*/
528 
529 /**
530  * @brief Convert an ASCII character to its corresponding hexadecimal value.
531  *        A :: block was found, now fill in the zero's.
532  * @param[in] pxSet  A set of variables describing the conversion.
533  */
prv_inet_pton6_set_zeros(struct sPTON6_Set * pxSet)534 static void prv_inet_pton6_set_zeros( struct sPTON6_Set * pxSet )
535 {
536     /* The number of bytes that were written after the :: */
537     const BaseType_t xCount = pxSet->xTargetIndex - pxSet->xColon;
538     const BaseType_t xTopIndex = ( BaseType_t ) ipSIZE_OF_IPv6_ADDRESS;
539     BaseType_t xIndex;
540     BaseType_t xTarget = xTopIndex - 1;
541     BaseType_t xSource = pxSet->xColon + ( xCount - 1 );
542 
543     /* Inserting 'xCount' zero's. */
544     for( xIndex = 0; xIndex < xCount; xIndex++ )
545     {
546         pxSet->pucTarget[ xTarget ] = pxSet->pucTarget[ xSource ];
547         pxSet->pucTarget[ xSource ] = 0;
548         xTarget--;
549         xSource--;
550     }
551 
552     pxSet->xTargetIndex = ( BaseType_t ) ipSIZE_OF_IPv6_ADDRESS;
553 }
554 /*-----------------------------------------------------------*/
555 
556 /**
557  * @brief Convert an IPv6 address in hexadecimal notation to a binary format of 16 bytes.
558  *
559  * @param[in] pcSource The address in hexadecimal notation.
560  * @param[out] pvDestination The address in binary format, 16 bytes long.
561  *
562  * @return The 32-bit representation of IP(v4) address.
563  */
FreeRTOS_inet_pton6(const char * pcSource,void * pvDestination)564 BaseType_t FreeRTOS_inet_pton6( const char * pcSource,
565                                 void * pvDestination )
566 {
567     char ch;
568     uint8_t ucNew;
569     BaseType_t xResult;
570     struct sPTON6_Set xSet;
571 
572     const char * pcIterator = pcSource;
573 
574     ( void ) memset( &( xSet ), 0, sizeof( xSet ) );
575     xSet.xColon = -1;
576     xSet.pucTarget = pvDestination;
577 
578     ( void ) memset( xSet.pucTarget, 0, ipSIZE_OF_IPv6_ADDRESS );
579 
580     xResult = 0;
581 
582     /* Leading :: requires some special handling. */
583     if( strcmp( pcIterator, "::" ) == 0 )
584     {
585         xResult = 1;
586     }
587     else
588     {
589         if( pcIterator[ 0 ] == ':' )
590         {
591             pcIterator++;
592         }
593 
594         /* The last bytes will be written at position 14 and 15. */
595         xSet.xHighestIndex = ( BaseType_t ) ipSIZE_OF_IPv6_ADDRESS;
596         xSet.xHighestIndex -= ( BaseType_t ) sizeof( uint16_t );
597 
598         /* The value in ulValue is not yet valid. */
599         xSet.xHadDigit = pdFALSE;
600         xSet.ulValue = 0U;
601 
602         for( ; ; )
603         {
604             ch = *( pcIterator );
605             pcIterator++;
606 
607             if( ch == ( char ) '\0' )
608             {
609                 /* The string is parsed now.
610                  * Store the last short, if present. */
611                 if( ( xSet.xHadDigit != pdFALSE ) &&
612                     ( xSet.xTargetIndex <= xSet.xHighestIndex ) )
613                 {
614                     /* Add the last value seen, network byte order ( MSB first ). */
615                     xSet.pucTarget[ xSet.xTargetIndex ] = ( uint8_t ) ( ( xSet.ulValue >> 8 ) & 0xffU );
616                     xSet.pucTarget[ xSet.xTargetIndex + 1 ] = ( uint8_t ) ( xSet.ulValue & 0xffU );
617                     xSet.xTargetIndex += 2;
618                 }
619 
620                 /* Break out of the for-ever loop. */
621                 break;
622             }
623 
624             /* Convert from a readable character to a hex value. */
625             ucNew = ucASCIIToHex( ch );
626             /* See if this is a digit or a colon. */
627             xResult = prv_inet_pton6_add_nibble( &( xSet ), ucNew, ch );
628 
629             if( xResult == pdFALSE )
630             {
631                 /* The new character was not accepted. */
632                 break;
633             }
634         } /* for( ;; ) */
635 
636         if( xSet.xColon >= 0 )
637         {
638             /* The address contains a block of zero. */
639             prv_inet_pton6_set_zeros( &( xSet ) );
640         }
641 
642         if( xSet.xTargetIndex == ( BaseType_t ) ipSIZE_OF_IPv6_ADDRESS )
643         {
644             xResult = 1;
645         }
646     }
647 
648     if( xResult != 1 )
649     {
650         xSet.pucTarget = ( uint8_t * ) pvDestination;
651         ( void ) memset( xSet.pucTarget, 0, ipSIZE_OF_IPv6_ADDRESS );
652     }
653 
654     return xResult;
655 }
656 
657 /*-----------------------------------------------------------*/
658 /* *INDENT-OFF* */
659 #endif /* ipconfigUSE_IPv6 != 0 */
660 /* *INDENT-ON* */
661