golang.zx2c4.com/wireguard/windows@v0.5.4-0.20230123132234-dcc0eb72a04b/ui/syntax/highlighter.go (about) 1 /* SPDX-License-Identifier: MIT 2 * 3 * Copyright (C) 2019-2022 WireGuard LLC. All Rights Reserved. 4 * 5 * This is a direct translation of the original C, and for that reason, it's pretty unusual Go code: 6 * https://git.zx2c4.com/wireguard-tools/tree/contrib/highlighter/highlighter.c 7 */ 8 9 package syntax 10 11 import "unsafe" 12 13 type highlight int 14 15 const ( 16 highlightSection highlight = iota 17 highlightField 18 highlightPrivateKey 19 highlightPublicKey 20 highlightPresharedKey 21 highlightIP 22 highlightCidr 23 highlightHost 24 highlightPort 25 highlightMTU 26 highlightKeepalive 27 highlightComment 28 highlightDelimiter 29 highlightTable 30 highlightCmd 31 highlightError 32 ) 33 34 func validateHighlight(isValid bool, t highlight) highlight { 35 if isValid { 36 return t 37 } 38 return highlightError 39 } 40 41 type highlightSpan struct { 42 t highlight 43 s int 44 len int 45 } 46 47 func isDecimal(c byte) bool { 48 return c >= '0' && c <= '9' 49 } 50 51 func isHexadecimal(c byte) bool { 52 return isDecimal(c) || (c|32) >= 'a' && (c|32) <= 'f' 53 } 54 55 func isAlphabet(c byte) bool { 56 return (c|32) >= 'a' && (c|32) <= 'z' 57 } 58 59 type stringSpan struct { 60 s *byte 61 len int 62 } 63 64 func (s stringSpan) at(i int) *byte { 65 return (*byte)(unsafe.Add(unsafe.Pointer(s.s), uintptr(i))) 66 } 67 68 func (s stringSpan) isSame(c string) bool { 69 if s.len != len(c) { 70 return false 71 } 72 cb := ([]byte)(c) 73 for i := 0; i < s.len; i++ { 74 if *s.at(i) != cb[i] { 75 return false 76 } 77 } 78 return true 79 } 80 81 func (s stringSpan) isCaselessSame(c string) bool { 82 if s.len != len(c) { 83 return false 84 } 85 cb := ([]byte)(c) 86 for i := 0; i < s.len; i++ { 87 a := *s.at(i) 88 b := cb[i] 89 if a-'a' < 26 { 90 a &= 95 91 } 92 if b-'a' < 26 { 93 b &= 95 94 } 95 if a != b { 96 return false 97 } 98 } 99 return true 100 } 101 102 func (s stringSpan) isValidKey() bool { 103 if s.len != 44 || *s.at(43) != '=' { 104 return false 105 } 106 for i := 0; i < 42; i++ { 107 if !isDecimal(*s.at(i)) && !isAlphabet(*s.at(i)) && *s.at(i) != '/' && *s.at(i) != '+' { 108 return false 109 } 110 } 111 switch *s.at(42) { 112 case 'A', 'E', 'I', 'M', 'Q', 'U', 'Y', 'c', 'g', 'k', 'o', 's', 'w', '4', '8', '0': 113 return true 114 } 115 return false 116 } 117 118 func (s stringSpan) isValidHostname() bool { 119 numDigit := 0 120 numEntity := s.len 121 if s.len > 63 || s.len == 0 { 122 return false 123 } 124 if *s.s == '-' || *s.at(s.len - 1) == '-' { 125 return false 126 } 127 if *s.s == '.' || *s.at(s.len - 1) == '.' { 128 return false 129 } 130 for i := 0; i < s.len; i++ { 131 if isDecimal(*s.at(i)) { 132 numDigit++ 133 continue 134 } 135 if *s.at(i) == '.' { 136 numEntity-- 137 continue 138 } 139 if !isAlphabet(*s.at(i)) && *s.at(i) != '-' { 140 return false 141 } 142 if i != 0 && *s.at(i) == '.' && *s.at(i - 1) == '.' { 143 return false 144 } 145 } 146 return numDigit != numEntity 147 } 148 149 func (s stringSpan) isValidIPv4() bool { 150 pos := 0 151 for i := 0; i < 4 && pos < s.len; i++ { 152 val := 0 153 j := 0 154 for ; j < 3 && pos+j < s.len && isDecimal(*s.at(pos + j)); j++ { 155 val = 10*val + int(*s.at(pos + j)-'0') 156 } 157 if j == 0 || j > 1 && *s.at(pos) == '0' || val > 255 { 158 return false 159 } 160 if pos+j == s.len && i == 3 { 161 return true 162 } 163 if *s.at(pos + j) != '.' { 164 return false 165 } 166 pos += j + 1 167 } 168 return false 169 } 170 171 func (s stringSpan) isValidIPv6() bool { 172 if s.len < 2 { 173 return false 174 } 175 pos := 0 176 if *s.at(0) == ':' { 177 if *s.at(1) != ':' { 178 return false 179 } 180 pos = 1 181 } 182 if *s.at(s.len - 1) == ':' && *s.at(s.len - 2) != ':' { 183 return false 184 } 185 seenColon := false 186 for i := 0; pos < s.len; i++ { 187 if *s.at(pos) == ':' && !seenColon { 188 seenColon = true 189 pos++ 190 if pos == s.len { 191 break 192 } 193 if i == 7 { 194 return false 195 } 196 continue 197 } 198 j := 0 199 for ; ; j++ { 200 if j < 4 && pos+j < s.len && isHexadecimal(*s.at(pos + j)) { 201 continue 202 } 203 break 204 } 205 if j == 0 { 206 return false 207 } 208 if pos+j == s.len && (seenColon || i == 7) { 209 break 210 } 211 if i == 7 { 212 return false 213 } 214 if *s.at(pos + j) != ':' { 215 if *s.at(pos + j) != '.' || i < 6 && !seenColon { 216 return false 217 } 218 return stringSpan{s.at(pos), s.len - pos}.isValidIPv4() 219 } 220 pos += j + 1 221 } 222 return true 223 } 224 225 func (s stringSpan) isValidUint(supportHex bool, min, max uint64) bool { 226 // Bound this around 32 bits, so that we don't have to write overflow logic. 227 if s.len > 10 || s.len == 0 { 228 return false 229 } 230 val := uint64(0) 231 if supportHex && s.len > 2 && *s.s == '0' && *s.at(1) == 'x' { 232 for i := 2; i < s.len; i++ { 233 if *s.at(i)-'0' < 10 { 234 val = 16*val + uint64(*s.at(i)-'0') 235 } else if (*s.at(i))|32-'a' < 6 { 236 val = 16*val + uint64((*s.at(i)|32)-'a'+10) 237 } else { 238 return false 239 } 240 } 241 } else { 242 for i := 0; i < s.len; i++ { 243 if !isDecimal(*s.at(i)) { 244 return false 245 } 246 val = 10*val + uint64(*s.at(i)-'0') 247 } 248 } 249 return val <= max && val >= min 250 } 251 252 func (s stringSpan) isValidPort() bool { 253 return s.isValidUint(false, 0, 65535) 254 } 255 256 func (s stringSpan) isValidMTU() bool { 257 return s.isValidUint(false, 576, 65535) 258 } 259 260 func (s stringSpan) isValidTable() bool { 261 return s.isSame("off") || s.isSame("auto") || s.isSame("main") || s.isValidUint(false, 0, (1<<32)-1) 262 } 263 264 func (s stringSpan) isValidPersistentKeepAlive() bool { 265 if s.isSame("off") { 266 return true 267 } 268 return s.isValidUint(false, 0, 65535) 269 } 270 271 // It's probably not worthwhile to try to validate a bash expression. So instead we just demand non-zero length. 272 func (s stringSpan) isValidPrePostUpDown() bool { 273 return s.len != 0 274 } 275 276 func (s stringSpan) isValidScope() bool { 277 if s.len > 64 || s.len == 0 { 278 return false 279 } 280 for i := 0; i < s.len; i++ { 281 if isAlphabet(*s.at(i)) && !isDecimal(*s.at(i)) && *s.at(i) != '_' && *s.at(i) != '=' && *s.at(i) != '+' && *s.at(i) != '.' && *s.at(i) != '-' { 282 return false 283 } 284 } 285 return true 286 } 287 288 func (s stringSpan) isValidEndpoint() bool { 289 if s.len == 0 { 290 return false 291 } 292 if *s.s == '[' { 293 seenScope := false 294 hostspan := stringSpan{s.at(1), 0} 295 for i := 1; i < s.len; i++ { 296 if *s.at(i) == '%' { 297 if seenScope { 298 return false 299 } 300 seenScope = true 301 if !hostspan.isValidIPv6() { 302 return false 303 } 304 hostspan = stringSpan{s.at(i + 1), 0} 305 } else if *s.at(i) == ']' { 306 if seenScope { 307 if !hostspan.isValidScope() { 308 return false 309 } 310 } else if !hostspan.isValidIPv6() { 311 return false 312 } 313 if i == s.len-1 || *s.at((i + 1)) != ':' { 314 return false 315 } 316 return stringSpan{s.at(i + 2), s.len - i - 2}.isValidPort() 317 } else { 318 hostspan.len++ 319 } 320 } 321 return false 322 } 323 for i := 0; i < s.len; i++ { 324 if *s.at(i) == ':' { 325 host := stringSpan{s.s, i} 326 port := stringSpan{s.at(i + 1), s.len - i - 1} 327 return port.isValidPort() && (host.isValidIPv4() || host.isValidHostname()) 328 } 329 } 330 return false 331 } 332 333 func (s stringSpan) isValidNetwork() bool { 334 for i := 0; i < s.len; i++ { 335 if *s.at(i) == '/' { 336 ip := stringSpan{s.s, i} 337 cidr := stringSpan{s.at(i + 1), s.len - i - 1} 338 cidrval := uint16(0) 339 if cidr.len > 3 || cidr.len == 0 { 340 return false 341 } 342 for j := 0; j < cidr.len; j++ { 343 if !isDecimal(*cidr.at(j)) { 344 return false 345 } 346 cidrval = 10*cidrval + uint16(*cidr.at(j)-'0') 347 } 348 if ip.isValidIPv4() { 349 return cidrval <= 32 350 } else if ip.isValidIPv6() { 351 return cidrval <= 128 352 } 353 return false 354 } 355 } 356 return s.isValidIPv4() || s.isValidIPv6() 357 } 358 359 type field int32 360 361 const ( 362 fieldInterfaceSection field = iota 363 fieldPrivateKey 364 fieldListenPort 365 fieldAddress 366 fieldDNS 367 fieldMTU 368 fieldTable 369 fieldPreUp 370 fieldPostUp 371 fieldPreDown 372 fieldPostDown 373 fieldPeerSection 374 fieldPublicKey 375 fieldPresharedKey 376 fieldAllowedIPs 377 fieldEndpoint 378 fieldPersistentKeepalive 379 fieldInvalid 380 ) 381 382 func sectionForField(t field) field { 383 if t > fieldInterfaceSection && t < fieldPeerSection { 384 return fieldInterfaceSection 385 } 386 if t > fieldPeerSection && t < fieldInvalid { 387 return fieldPeerSection 388 } 389 return fieldInvalid 390 } 391 392 func (s stringSpan) field() field { 393 switch { 394 case s.isCaselessSame("PrivateKey"): 395 return fieldPrivateKey 396 case s.isCaselessSame("ListenPort"): 397 return fieldListenPort 398 case s.isCaselessSame("Address"): 399 return fieldAddress 400 case s.isCaselessSame("DNS"): 401 return fieldDNS 402 case s.isCaselessSame("MTU"): 403 return fieldMTU 404 case s.isCaselessSame("Table"): 405 return fieldTable 406 case s.isCaselessSame("PublicKey"): 407 return fieldPublicKey 408 case s.isCaselessSame("PresharedKey"): 409 return fieldPresharedKey 410 case s.isCaselessSame("AllowedIPs"): 411 return fieldAllowedIPs 412 case s.isCaselessSame("Endpoint"): 413 return fieldEndpoint 414 case s.isCaselessSame("PersistentKeepalive"): 415 return fieldPersistentKeepalive 416 case s.isCaselessSame("PreUp"): 417 return fieldPreUp 418 case s.isCaselessSame("PostUp"): 419 return fieldPostUp 420 case s.isCaselessSame("PreDown"): 421 return fieldPreDown 422 case s.isCaselessSame("PostDown"): 423 return fieldPostDown 424 } 425 return fieldInvalid 426 } 427 428 func (s stringSpan) sectionType() field { 429 switch { 430 case s.isCaselessSame("[Peer]"): 431 return fieldPeerSection 432 case s.isCaselessSame("[Interface]"): 433 return fieldInterfaceSection 434 } 435 return fieldInvalid 436 } 437 438 type highlightSpanArray []highlightSpan 439 440 func (hsa *highlightSpanArray) append(o *byte, s stringSpan, t highlight) { 441 if s.len == 0 { 442 return 443 } 444 *hsa = append(*hsa, highlightSpan{t, int((uintptr(unsafe.Pointer(s.s))) - (uintptr(unsafe.Pointer(o)))), s.len}) 445 } 446 447 func (hsa *highlightSpanArray) highlightMultivalueValue(parent, s stringSpan, section field) { 448 switch section { 449 case fieldDNS: 450 if s.isValidIPv4() || s.isValidIPv6() { 451 hsa.append(parent.s, s, highlightIP) 452 } else if s.isValidHostname() { 453 hsa.append(parent.s, s, highlightHost) 454 } else { 455 hsa.append(parent.s, s, highlightError) 456 } 457 case fieldAddress, fieldAllowedIPs: 458 if !s.isValidNetwork() { 459 hsa.append(parent.s, s, highlightError) 460 break 461 } 462 slash := 0 463 for ; slash < s.len; slash++ { 464 if *s.at(slash) == '/' { 465 break 466 } 467 } 468 if slash == s.len { 469 hsa.append(parent.s, s, highlightIP) 470 } else { 471 hsa.append(parent.s, stringSpan{s.s, slash}, highlightIP) 472 hsa.append(parent.s, stringSpan{s.at(slash), 1}, highlightDelimiter) 473 hsa.append(parent.s, stringSpan{s.at(slash + 1), s.len - slash - 1}, highlightCidr) 474 } 475 default: 476 hsa.append(parent.s, s, highlightError) 477 } 478 } 479 480 func (hsa *highlightSpanArray) highlightMultivalue(parent, s stringSpan, section field) { 481 currentSpan := stringSpan{s.s, 0} 482 lenAtLastSpace := 0 483 for i := 0; i < s.len; i++ { 484 if *s.at(i) == ',' { 485 currentSpan.len = lenAtLastSpace 486 hsa.highlightMultivalueValue(parent, currentSpan, section) 487 hsa.append(parent.s, stringSpan{s.at(i), 1}, highlightDelimiter) 488 lenAtLastSpace = 0 489 currentSpan = stringSpan{s.at(i + 1), 0} 490 } else if *s.at(i) == ' ' || *s.at(i) == '\t' { 491 if s.at(i) == currentSpan.s && currentSpan.len == 0 { 492 currentSpan.s = currentSpan.at(1) 493 } else { 494 currentSpan.len++ 495 } 496 } else { 497 currentSpan.len++ 498 lenAtLastSpace = currentSpan.len 499 } 500 } 501 currentSpan.len = lenAtLastSpace 502 if currentSpan.len != 0 { 503 hsa.highlightMultivalueValue(parent, currentSpan, section) 504 } else if (*hsa)[len(*hsa)-1].t == highlightDelimiter { 505 (*hsa)[len(*hsa)-1].t = highlightError 506 } 507 } 508 509 func (hsa *highlightSpanArray) highlightValue(parent, s stringSpan, section field) { 510 switch section { 511 case fieldPrivateKey: 512 hsa.append(parent.s, s, validateHighlight(s.isValidKey(), highlightPrivateKey)) 513 case fieldPublicKey: 514 hsa.append(parent.s, s, validateHighlight(s.isValidKey(), highlightPublicKey)) 515 case fieldPresharedKey: 516 hsa.append(parent.s, s, validateHighlight(s.isValidKey(), highlightPresharedKey)) 517 case fieldMTU: 518 hsa.append(parent.s, s, validateHighlight(s.isValidMTU(), highlightMTU)) 519 case fieldTable: 520 hsa.append(parent.s, s, validateHighlight(s.isValidTable(), highlightTable)) 521 case fieldPreUp, fieldPostUp, fieldPreDown, fieldPostDown: 522 hsa.append(parent.s, s, validateHighlight(s.isValidPrePostUpDown(), highlightCmd)) 523 case fieldListenPort: 524 hsa.append(parent.s, s, validateHighlight(s.isValidPort(), highlightPort)) 525 case fieldPersistentKeepalive: 526 hsa.append(parent.s, s, validateHighlight(s.isValidPersistentKeepAlive(), highlightKeepalive)) 527 case fieldEndpoint: 528 if !s.isValidEndpoint() { 529 hsa.append(parent.s, s, highlightError) 530 break 531 } 532 colon := s.len 533 for colon > 0 { 534 colon-- 535 if *s.at(colon) == ':' { 536 break 537 } 538 } 539 hsa.append(parent.s, stringSpan{s.s, colon}, highlightHost) 540 hsa.append(parent.s, stringSpan{s.at(colon), 1}, highlightDelimiter) 541 hsa.append(parent.s, stringSpan{s.at(colon + 1), s.len - colon - 1}, highlightPort) 542 case fieldAddress, fieldDNS, fieldAllowedIPs: 543 hsa.highlightMultivalue(parent, s, section) 544 default: 545 hsa.append(parent.s, s, highlightError) 546 } 547 } 548 549 func highlightConfig(config string) []highlightSpan { 550 var ret highlightSpanArray 551 b := append([]byte(config), 0) 552 s := stringSpan{&b[0], len(b) - 1} 553 currentSpan := stringSpan{s.s, 0} 554 currentSection := fieldInvalid 555 currentField := fieldInvalid 556 const ( 557 onNone = iota 558 onKey 559 onValue 560 onComment 561 onSection 562 ) 563 state := onNone 564 lenAtLastSpace := 0 565 equalsLocation := 0 566 for i := 0; i <= s.len; i++ { 567 if i == s.len || *s.at(i) == '\n' || state != onComment && *s.at(i) == '#' { 568 if state == onKey { 569 currentSpan.len = lenAtLastSpace 570 ret.append(s.s, currentSpan, highlightError) 571 } else if state == onValue { 572 if currentSpan.len != 0 { 573 ret.append(s.s, stringSpan{s.at(equalsLocation), 1}, highlightDelimiter) 574 currentSpan.len = lenAtLastSpace 575 ret.highlightValue(s, currentSpan, currentField) 576 } else { 577 ret.append(s.s, stringSpan{s.at(equalsLocation), 1}, highlightError) 578 } 579 } else if state == onSection { 580 currentSpan.len = lenAtLastSpace 581 currentSection = currentSpan.sectionType() 582 ret.append(s.s, currentSpan, validateHighlight(currentSection != fieldInvalid, highlightSection)) 583 } else if state == onComment { 584 ret.append(s.s, currentSpan, highlightComment) 585 } 586 if i == s.len { 587 break 588 } 589 lenAtLastSpace = 0 590 currentField = fieldInvalid 591 if *s.at(i) == '#' { 592 currentSpan = stringSpan{s.at(i), 1} 593 state = onComment 594 } else { 595 currentSpan = stringSpan{s.at(i + 1), 0} 596 state = onNone 597 } 598 } else if state == onComment { 599 currentSpan.len++ 600 } else if *s.at(i) == ' ' || *s.at(i) == '\t' { 601 if s.at(i) == currentSpan.s && currentSpan.len == 0 { 602 currentSpan.s = currentSpan.at(1) 603 } else { 604 currentSpan.len++ 605 } 606 } else if *s.at(i) == '=' && state == onKey { 607 currentSpan.len = lenAtLastSpace 608 currentField = currentSpan.field() 609 section := sectionForField(currentField) 610 if section == fieldInvalid || currentField == fieldInvalid || section != currentSection { 611 ret.append(s.s, currentSpan, highlightError) 612 } else { 613 ret.append(s.s, currentSpan, highlightField) 614 } 615 equalsLocation = i 616 currentSpan = stringSpan{s.at(i + 1), 0} 617 state = onValue 618 } else { 619 if state == onNone { 620 if *s.at(i) == '[' { 621 state = onSection 622 } else { 623 state = onKey 624 } 625 } 626 currentSpan.len++ 627 lenAtLastSpace = currentSpan.len 628 } 629 } 630 return ([]highlightSpan)(ret) 631 }