]> git.proxmox.com Git - mirror_edk2.git/blame - MdeModulePkg/Universal/Network/Ip4Dxe/Ip4Igmp.c
add security check.
[mirror_edk2.git] / MdeModulePkg / Universal / Network / Ip4Dxe / Ip4Igmp.c
CommitLineData
772db4bb 1/** @file\r
3e8c18da 2 This file implements the RFC2236: IGMP v2.\r
3 \r
894d038a 4Copyright (c) 2005 - 2009, Intel Corporation.<BR>\r
772db4bb 5All rights reserved. This program and the accompanying materials\r
6are licensed and made available under the terms and conditions of the BSD License\r
7which accompanies this distribution. The full text of the license may be found at\r
8http://opensource.org/licenses/bsd-license.php\r
9\r
10THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
11WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
12\r
772db4bb 13**/\r
14\r
15#include "Ip4Impl.h"\r
16\r
17//\r
18// Route Alert option in IGMP report to direct routers to\r
19// examine the packet more closely.\r
20//\r
21UINT32 mRouteAlertOption = 0x00000494;\r
22\r
23\r
24/**\r
25 Init the IGMP control data of the IP4 service instance, configure\r
26 MNP to receive ALL SYSTEM multicast.\r
27\r
3e8c18da 28 @param[in, out] IpSb The IP4 service whose IGMP is to be initialized.\r
772db4bb 29\r
5405e9a6 30 @retval EFI_SUCCESS IGMP of the IpSb is successfully initialized.\r
772db4bb 31 @retval EFI_OUT_OF_RESOURCES Failed to allocate resource to initialize IGMP.\r
32 @retval Others Failed to initialize the IGMP of IpSb.\r
772db4bb 33\r
34**/\r
35EFI_STATUS\r
36Ip4InitIgmp (\r
5405e9a6 37 IN OUT IP4_SERVICE *IpSb\r
772db4bb 38 )\r
39{\r
40 IGMP_SERVICE_DATA *IgmpCtrl;\r
41 EFI_MANAGED_NETWORK_PROTOCOL *Mnp;\r
42 IGMP_GROUP *Group;\r
43 EFI_STATUS Status;\r
44\r
45 IgmpCtrl = &IpSb->IgmpCtrl;\r
46\r
47 //\r
48 // Configure MNP to receive ALL_SYSTEM multicast\r
49 //\r
e48e37fc 50 Group = AllocatePool (sizeof (IGMP_GROUP));\r
772db4bb 51\r
52 if (Group == NULL) {\r
53 return EFI_OUT_OF_RESOURCES;\r
54 }\r
55\r
56 Mnp = IpSb->Mnp;\r
57\r
58 Group->Address = IP4_ALLSYSTEM_ADDRESS;\r
59 Group->RefCnt = 1;\r
60 Group->DelayTime = 0;\r
61 Group->ReportByUs = FALSE;\r
62\r
63 Status = Ip4GetMulticastMac (Mnp, IP4_ALLSYSTEM_ADDRESS, &Group->Mac);\r
64\r
65 if (EFI_ERROR (Status)) {\r
66 goto ON_ERROR;\r
67 }\r
68\r
69 Status = Mnp->Groups (Mnp, TRUE, &Group->Mac);\r
70\r
71 if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {\r
72 goto ON_ERROR;\r
73 }\r
74\r
e48e37fc 75 InsertHeadList (&IgmpCtrl->Groups, &Group->Link);\r
772db4bb 76 return EFI_SUCCESS;\r
77\r
78ON_ERROR:\r
e48e37fc 79 gBS->FreePool (Group);\r
772db4bb 80 return Status;\r
81}\r
82\r
83\r
84/**\r
85 Find the IGMP_GROUP structure which contains the status of multicast\r
86 group Address in this IGMP control block\r
87\r
3e8c18da 88 @param[in] IgmpCtrl The IGMP control block to search from\r
89 @param[in] Address The multicast address to search\r
772db4bb 90\r
91 @return NULL if the multicast address isn't in the IGMP control block. Otherwise\r
5405e9a6 92 the point to the IGMP_GROUP which contains the status of multicast group\r
93 for Address.\r
772db4bb 94\r
95**/\r
96IGMP_GROUP *\r
97Ip4FindGroup (\r
98 IN IGMP_SERVICE_DATA *IgmpCtrl,\r
99 IN IP4_ADDR Address\r
100 )\r
101{\r
e48e37fc 102 LIST_ENTRY *Entry;\r
772db4bb 103 IGMP_GROUP *Group;\r
104\r
105 NET_LIST_FOR_EACH (Entry, &IgmpCtrl->Groups) {\r
106 Group = NET_LIST_USER_STRUCT (Entry, IGMP_GROUP, Link);\r
107\r
108 if (Group->Address == Address) {\r
109 return Group;\r
110 }\r
111 }\r
112\r
113 return NULL;\r
114}\r
115\r
116\r
117/**\r
118 Count the number of IP4 multicast groups that are mapped to the\r
119 same MAC address. Several IP4 multicast address may be mapped to\r
120 the same MAC address.\r
121\r
3e8c18da 122 @param[in] IgmpCtrl The IGMP control block to search in\r
123 @param[in] Mac The MAC address to search\r
772db4bb 124\r
125 @return The number of the IP4 multicast group that mapped to the same\r
5405e9a6 126 multicast group Mac.\r
772db4bb 127\r
128**/\r
129INTN\r
130Ip4FindMac (\r
131 IN IGMP_SERVICE_DATA *IgmpCtrl,\r
132 IN EFI_MAC_ADDRESS *Mac\r
133 )\r
134{\r
e48e37fc 135 LIST_ENTRY *Entry;\r
772db4bb 136 IGMP_GROUP *Group;\r
137 INTN Count;\r
138\r
139 Count = 0;\r
140\r
141 NET_LIST_FOR_EACH (Entry, &IgmpCtrl->Groups) {\r
142 Group = NET_LIST_USER_STRUCT (Entry, IGMP_GROUP, Link);\r
143\r
144 if (NET_MAC_EQUAL (&Group->Mac, Mac, sizeof (EFI_MAC_ADDRESS))) {\r
145 Count++;\r
146 }\r
147 }\r
148\r
149 return Count;\r
150}\r
151\r
152\r
153/**\r
154 Send an IGMP protocol message to the Dst, such as IGMP v1 membership report.\r
155\r
3e8c18da 156 @param[in] IpSb The IP4 service instance that requests the\r
772db4bb 157 transmission\r
3e8c18da 158 @param[in] Dst The destinaton to send to\r
159 @param[in] Type The IGMP message type, such as IGMP v1 membership\r
772db4bb 160 report\r
3e8c18da 161 @param[in] Group The group address in the IGMP message head.\r
772db4bb 162\r
163 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to build the message\r
164 @retval EFI_SUCCESS The IGMP message is successfully send\r
165 @retval Others Failed to send the IGMP message.\r
166\r
167**/\r
168EFI_STATUS\r
169Ip4SendIgmpMessage (\r
170 IN IP4_SERVICE *IpSb,\r
171 IN IP4_ADDR Dst,\r
172 IN UINT8 Type,\r
173 IN IP4_ADDR Group\r
174 )\r
175{\r
176 IP4_HEAD Head;\r
177 NET_BUF *Packet;\r
178 IGMP_HEAD *Igmp;\r
179\r
180 //\r
181 // Allocate a net buffer to hold the message\r
182 //\r
183 Packet = NetbufAlloc (IP4_MAX_HEADLEN + sizeof (IGMP_HEAD));\r
184\r
185 if (Packet == NULL) {\r
186 return EFI_OUT_OF_RESOURCES;\r
187 }\r
188\r
189 //\r
190 // Fill in the IGMP and IP header, then transmit the message\r
191 //\r
192 NetbufReserve (Packet, IP4_MAX_HEADLEN);\r
193\r
194 Igmp = (IGMP_HEAD *) NetbufAllocSpace (Packet, sizeof (IGMP_HEAD), FALSE);\r
894d038a 195 if (Igmp == NULL) {\r
196 return EFI_OUT_OF_RESOURCES;\r
197 }\r
772db4bb 198\r
199 Igmp->Type = Type;\r
200 Igmp->MaxRespTime = 0;\r
201 Igmp->Checksum = 0;\r
202 Igmp->Group = HTONL (Group);\r
687a2e5f 203 Igmp->Checksum = (UINT16) (~NetblockChecksum ((UINT8 *) Igmp, sizeof (IGMP_HEAD)));\r
772db4bb 204\r
205 Head.Tos = 0;\r
206 Head.Protocol = IP4_PROTO_IGMP;\r
207 Head.Ttl = 1;\r
208 Head.Fragment = 0;\r
209 Head.Dst = Dst;\r
210 Head.Src = IP4_ALLZERO_ADDRESS;\r
211\r
212 return Ip4Output (\r
213 IpSb,\r
214 NULL,\r
215 Packet,\r
216 &Head,\r
217 (UINT8 *) &mRouteAlertOption,\r
218 sizeof (UINT32),\r
219 IP4_ALLZERO_ADDRESS,\r
220 Ip4SysPacketSent,\r
221 NULL\r
222 );\r
223}\r
224\r
225\r
226/**\r
227 Send an IGMP membership report. Depends on whether the server is\r
228 v1 or v2, it will send either a V1 or V2 membership report.\r
229\r
3e8c18da 230 @param[in] IpSb The IP4 service instance that requests the\r
772db4bb 231 transmission.\r
3e8c18da 232 @param[in] Group The group address to report\r
772db4bb 233\r
234 @retval EFI_OUT_OF_RESOURCES Failed to allocate memory to build the message\r
235 @retval EFI_SUCCESS The IGMP report message is successfully send\r
236 @retval Others Failed to send the report.\r
237\r
238**/\r
239EFI_STATUS\r
240Ip4SendIgmpReport (\r
241 IN IP4_SERVICE *IpSb,\r
242 IN IP4_ADDR Group\r
243 )\r
244{\r
245 if (IpSb->IgmpCtrl.Igmpv1QuerySeen != 0) {\r
246 return Ip4SendIgmpMessage (IpSb, Group, IGMP_V1_MEMBERSHIP_REPORT, Group);\r
247 } else {\r
248 return Ip4SendIgmpMessage (IpSb, Group, IGMP_V2_MEMBERSHIP_REPORT, Group);\r
249 }\r
250}\r
251\r
252\r
253/**\r
5405e9a6 254 Join the multicast group on behalf of this IP4 child\r
772db4bb 255\r
3e8c18da 256 @param[in] IpInstance The IP4 child that wants to join the group\r
257 @param[in] Address The group to join\r
772db4bb 258\r
259 @retval EFI_SUCCESS Successfully join the multicast group\r
260 @retval EFI_OUT_OF_RESOURCES Failed to allocate resources\r
261 @retval Others Failed to join the multicast group.\r
262\r
263**/\r
264EFI_STATUS\r
265Ip4JoinGroup (\r
266 IN IP4_PROTOCOL *IpInstance,\r
267 IN IP4_ADDR Address\r
268 )\r
269{\r
270 EFI_MANAGED_NETWORK_PROTOCOL *Mnp;\r
271 IP4_SERVICE *IpSb;\r
272 IGMP_SERVICE_DATA *IgmpCtrl;\r
273 IGMP_GROUP *Group;\r
274 EFI_STATUS Status;\r
275\r
276 IpSb = IpInstance->Service;\r
277 IgmpCtrl = &IpSb->IgmpCtrl;\r
278 Mnp = IpSb->Mnp;\r
279\r
280 //\r
281 // If the IP service already is a member in the group, just\r
282 // increase the refernce count and return.\r
283 //\r
284 Group = Ip4FindGroup (IgmpCtrl, Address);\r
285\r
286 if (Group != NULL) {\r
287 Group->RefCnt++;\r
288 return EFI_SUCCESS;\r
289 }\r
290\r
291 //\r
292 // Otherwise, create a new IGMP_GROUP, Get the multicast's MAC address,\r
293 // send a report, then direct MNP to receive the multicast.\r
294 //\r
e48e37fc 295 Group = AllocatePool (sizeof (IGMP_GROUP));\r
772db4bb 296\r
297 if (Group == NULL) {\r
298 return EFI_OUT_OF_RESOURCES;\r
299 }\r
300\r
301 Group->Address = Address;\r
302 Group->RefCnt = 1;\r
303 Group->DelayTime = IGMP_UNSOLICIATED_REPORT;\r
304 Group->ReportByUs = TRUE;\r
305\r
306 Status = Ip4GetMulticastMac (Mnp, Address, &Group->Mac);\r
307\r
308 if (EFI_ERROR (Status)) {\r
309 goto ON_ERROR;\r
310 }\r
311\r
312 Status = Ip4SendIgmpReport (IpSb, Address);\r
313\r
314 if (EFI_ERROR (Status)) {\r
315 goto ON_ERROR;\r
316 }\r
317\r
318 Status = Mnp->Groups (Mnp, TRUE, &Group->Mac);\r
319\r
320 if (EFI_ERROR (Status) && (Status != EFI_ALREADY_STARTED)) {\r
321 goto ON_ERROR;\r
322 }\r
323\r
e48e37fc 324 InsertHeadList (&IgmpCtrl->Groups, &Group->Link);\r
772db4bb 325 return EFI_SUCCESS;\r
326\r
327ON_ERROR:\r
e48e37fc 328 gBS->FreePool (Group);\r
772db4bb 329 return Status;\r
330}\r
331\r
332\r
333/**\r
5405e9a6 334 Leave the IP4 multicast group on behalf of IpInstance.\r
772db4bb 335\r
3e8c18da 336 @param[in] IpInstance The IP4 child that wants to leave the group\r
772db4bb 337 address\r
3e8c18da 338 @param[in] Address The group address to leave\r
772db4bb 339\r
340 @retval EFI_NOT_FOUND The IP4 service instance isn't in the group\r
341 @retval EFI_SUCCESS Successfully leave the multicast group.\r
342 @retval Others Failed to leave the multicast group.\r
343\r
344**/\r
345EFI_STATUS\r
346Ip4LeaveGroup (\r
347 IN IP4_PROTOCOL *IpInstance,\r
348 IN IP4_ADDR Address\r
349 )\r
350{\r
351 EFI_MANAGED_NETWORK_PROTOCOL *Mnp;\r
352 IP4_SERVICE *IpSb;\r
353 IGMP_SERVICE_DATA *IgmpCtrl;\r
354 IGMP_GROUP *Group;\r
355 EFI_STATUS Status;\r
356\r
357 IpSb = IpInstance->Service;\r
358 IgmpCtrl = &IpSb->IgmpCtrl;\r
359 Mnp = IpSb->Mnp;\r
360\r
361 Group = Ip4FindGroup (IgmpCtrl, Address);\r
362\r
363 if (Group == NULL) {\r
364 return EFI_NOT_FOUND;\r
365 }\r
366\r
367 //\r
368 // If more than one instance is in the group, decrease\r
369 // the RefCnt then return.\r
370 //\r
371 if (--Group->RefCnt > 0) {\r
372 return EFI_SUCCESS;\r
373 }\r
374\r
375 //\r
376 // If multiple IP4 group addresses are mapped to the same\r
377 // multicast MAC address, don't configure the MNP to leave\r
378 // the MAC.\r
379 //\r
380 if (Ip4FindMac (IgmpCtrl, &Group->Mac) == 1) {\r
381 Status = Mnp->Groups (Mnp, FALSE, &Group->Mac);\r
382\r
383 if (EFI_ERROR (Status) && (Status != EFI_NOT_FOUND)) {\r
384 return Status;\r
385 }\r
386 }\r
387\r
388 //\r
389 // Send a leave report if the membership is reported by us\r
390 // and we are talking IGMPv2.\r
391 //\r
5405e9a6 392 if (Group->ReportByUs && IgmpCtrl->Igmpv1QuerySeen == 0) {\r
772db4bb 393 Ip4SendIgmpMessage (IpSb, IP4_ALLROUTER_ADDRESS, IGMP_LEAVE_GROUP, Group->Address);\r
394 }\r
395\r
e48e37fc 396 RemoveEntryList (&Group->Link);\r
397 gBS->FreePool (Group);\r
772db4bb 398\r
399 return EFI_SUCCESS;\r
400}\r
401\r
402\r
403/**\r
404 Handle the received IGMP message for the IP4 service instance.\r
405\r
3e8c18da 406 @param[in] IpSb The IP4 service instance that received the message\r
407 @param[in] Head The IP4 header of the received message\r
408 @param[in] Packet The IGMP message, without IP4 header\r
772db4bb 409\r
410 @retval EFI_INVALID_PARAMETER The IGMP message is malformated.\r
411 @retval EFI_SUCCESS The IGMP message is successfully processed.\r
412\r
413**/\r
414EFI_STATUS\r
415Ip4IgmpHandle (\r
416 IN IP4_SERVICE *IpSb,\r
417 IN IP4_HEAD *Head,\r
418 IN NET_BUF *Packet\r
419 )\r
420{\r
421 IGMP_SERVICE_DATA *IgmpCtrl;\r
422 IGMP_HEAD Igmp;\r
423 IGMP_GROUP *Group;\r
424 IP4_ADDR Address;\r
e48e37fc 425 LIST_ENTRY *Entry;\r
772db4bb 426\r
427 IgmpCtrl = &IpSb->IgmpCtrl;\r
428\r
429 //\r
430 // Must checksum over the whole packet, later IGMP version\r
431 // may employ message longer than 8 bytes. IP's header has\r
432 // already been trimmed off.\r
433 //\r
434 if ((Packet->TotalSize < sizeof (Igmp)) || (NetbufChecksum (Packet) != 0)) {\r
435 NetbufFree (Packet);\r
436 return EFI_INVALID_PARAMETER;\r
437 }\r
438\r
439 //\r
440 // Copy the packet in case it is fragmented\r
441 //\r
442 NetbufCopy (Packet, 0, sizeof (IGMP_HEAD), (UINT8 *)&Igmp);\r
443\r
444 switch (Igmp.Type) {\r
445 case IGMP_MEMBERSHIP_QUERY:\r
446 //\r
5405e9a6 447 // If MaxRespTime is zero, it is most likely that we are\r
772db4bb 448 // talking to a V1 router\r
449 //\r
450 if (Igmp.MaxRespTime == 0) {\r
451 IgmpCtrl->Igmpv1QuerySeen = IGMP_V1ROUTER_PRESENT;\r
452 Igmp.MaxRespTime = 100;\r
453 }\r
454\r
455 //\r
456 // Igmp is ticking once per second but MaxRespTime is in\r
457 // the unit of 100ms.\r
458 //\r
459 Igmp.MaxRespTime /= 10;\r
460 Address = NTOHL (Igmp.Group);\r
461\r
462 if (Address == IP4_ALLSYSTEM_ADDRESS) {\r
463 break;\r
464 }\r
465\r
466 NET_LIST_FOR_EACH (Entry, &IgmpCtrl->Groups) {\r
467 Group = NET_LIST_USER_STRUCT (Entry, IGMP_GROUP, Link);\r
468\r
469 //\r
470 // If address is all zero, all the memberships will be reported.\r
471 // otherwise only one is reported.\r
472 //\r
473 if ((Address == IP4_ALLZERO_ADDRESS) || (Address == Group->Address)) {\r
474 //\r
475 // If the timer is pending, only update it if the time left\r
476 // is longer than the MaxRespTime. TODO: randomize the DelayTime.\r
477 //\r
478 if ((Group->DelayTime == 0) || (Group->DelayTime > Igmp.MaxRespTime)) {\r
36ee91ca 479 Group->DelayTime = MAX (1, Igmp.MaxRespTime);\r
772db4bb 480 }\r
481 }\r
482 }\r
483\r
484 break;\r
485\r
486 case IGMP_V1_MEMBERSHIP_REPORT:\r
487 case IGMP_V2_MEMBERSHIP_REPORT:\r
488 Address = NTOHL (Igmp.Group);\r
489 Group = Ip4FindGroup (IgmpCtrl, Address);\r
490\r
491 if ((Group != NULL) && (Group->DelayTime > 0)) {\r
492 Group->DelayTime = 0;\r
493 Group->ReportByUs = FALSE;\r
494 }\r
495\r
496 break;\r
497 }\r
498\r
499 NetbufFree (Packet);\r
500 return EFI_SUCCESS;\r
501}\r
502\r
503\r
504/**\r
505 The periodical timer function for IGMP. It does the following\r
506 things:\r
507 1. Decrease the Igmpv1QuerySeen to make it possible to refresh\r
5405e9a6 508 the IGMP server type.\r
772db4bb 509 2. Decrease the report timer for each IGMP group in "delaying\r
5405e9a6 510 member" state.\r
772db4bb 511\r
3e8c18da 512 @param[in] IpSb The IP4 service instance that is ticking\r
772db4bb 513\r
514**/\r
515VOID\r
516Ip4IgmpTicking (\r
517 IN IP4_SERVICE *IpSb\r
518 )\r
519{\r
520 IGMP_SERVICE_DATA *IgmpCtrl;\r
e48e37fc 521 LIST_ENTRY *Entry;\r
772db4bb 522 IGMP_GROUP *Group;\r
523\r
524 IgmpCtrl = &IpSb->IgmpCtrl;\r
525\r
526 if (IgmpCtrl->Igmpv1QuerySeen > 0) {\r
527 IgmpCtrl->Igmpv1QuerySeen--;\r
528 }\r
529\r
530 //\r
531 // Decrease the report timer for each IGMP group in "delaying member"\r
532 //\r
533 NET_LIST_FOR_EACH (Entry, &IgmpCtrl->Groups) {\r
534 Group = NET_LIST_USER_STRUCT (Entry, IGMP_GROUP, Link);\r
535 ASSERT (Group->DelayTime >= 0);\r
536\r
537 if (Group->DelayTime > 0) {\r
538 Group->DelayTime--;\r
539\r
540 if (Group->DelayTime == 0) {\r
541 Ip4SendIgmpReport (IpSb, Group->Address);\r
542 Group->ReportByUs = TRUE;\r
543 }\r
544 }\r
545 }\r
546}\r
547\r
548\r
549/**\r
550 Add a group address to the array of group addresses.\r
551 The caller should make sure that no duplicated address\r
552 existed in the array. Although the function doesn't\r
553 assume the byte order of the both Source and Addr, the\r
554 network byte order is used by the caller.\r
555\r
3e8c18da 556 @param[in] Source The array of group addresses to add to\r
557 @param[in] Count The number of group addresses in the Source\r
558 @param[in] Addr The IP4 multicast address to add\r
772db4bb 559\r
560 @return NULL if failed to allocate memory for the new groups,\r
5405e9a6 561 otherwise the new combined group addresses.\r
772db4bb 562\r
563**/\r
564IP4_ADDR *\r
565Ip4CombineGroups (\r
566 IN IP4_ADDR *Source,\r
567 IN UINT32 Count,\r
568 IN IP4_ADDR Addr\r
569 )\r
570{\r
571 IP4_ADDR *Groups;\r
572\r
e48e37fc 573 Groups = AllocatePool (sizeof (IP4_ADDR) * (Count + 1));\r
772db4bb 574\r
575 if (Groups == NULL) {\r
576 return NULL;\r
577 }\r
578\r
e48e37fc 579 CopyMem (Groups, Source, Count * sizeof (IP4_ADDR));\r
772db4bb 580 Groups[Count] = Addr;\r
581\r
582 return Groups;\r
583}\r
584\r
585\r
586/**\r
5405e9a6 587 Remove a group address from the array of group addresses.\r
772db4bb 588 Although the function doesn't assume the byte order of the\r
589 both Groups and Addr, the network byte order is used by\r
590 the caller.\r
591\r
3e8c18da 592 @param Groups The array of group addresses to remove from\r
593 @param Count The number of group addresses in the Groups\r
594 @param Addr The IP4 multicast address to remove\r
772db4bb 595\r
596 @return The nubmer of group addresses in the Groups after remove.\r
5405e9a6 597 It is Count if the Addr isn't in the Groups.\r
772db4bb 598\r
599**/\r
600INTN\r
601Ip4RemoveGroupAddr (\r
5405e9a6 602 IN OUT IP4_ADDR *Groups,\r
603 IN UINT32 Count,\r
604 IN IP4_ADDR Addr\r
772db4bb 605 )\r
606{\r
607 UINT32 Index;\r
608\r
609 for (Index = 0; Index < Count; Index++) {\r
610 if (Groups[Index] == Addr) {\r
611 break;\r
612 }\r
613 }\r
614\r
615 while (Index < Count - 1) {\r
616 Groups[Index] = Groups[Index + 1];\r
617 Index++;\r
618 }\r
619\r
620 return Index;\r
621}\r