Skip to content

Instantly share code, notes, and snippets.

@arr2036
Created May 7, 2014 13:16
Show Gist options
  • Save arr2036/c2d7ab292ccf8d3b780b to your computer and use it in GitHub Desktop.
Save arr2036/c2d7ab292ccf8d3b780b to your computer and use it in GitHub Desktop.
Convert a string of hexits to FreeRADIUS escaped octal string
/** Convert hexits to FR escaped ascii string
*
* This utility is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
* @note Much of this code was derived from code in the FreeRADIUS project
* @note hence using the GPL.
*/
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stdbool.h>
#include <ctype.h>
int utf8_char(uint8_t const *str)
{
if (*str < 0x20) return 0;
if (*str <= 0x7e) return 1; /* 1 */
if (*str <= 0xc1) return 0;
if ((str[0] >= 0xc2) && /* 2 */
(str[0] <= 0xdf) &&
(str[1] >= 0x80) &&
(str[1] <= 0xbf)) {
return 2;
}
if ((str[0] == 0xe0) && /* 3 */
(str[1] >= 0xa0) &&
(str[1] <= 0xbf) &&
(str[2] >= 0x80) &&
(str[2] <= 0xbf)) {
return 3;
}
if ((str[0] >= 0xe1) && /* 4a */
(str[0] <= 0xec) &&
(str[1] >= 0x80) &&
(str[1] <= 0xbf) &&
(str[2] >= 0x80) &&
(str[2] <= 0xbf)) {
return 3;
}
if ((str[0] >= 0xee) && /* 4b */
(str[0] <= 0xef) &&
(str[1] >= 0x80) &&
(str[1] <= 0xbf) &&
(str[2] >= 0x80) &&
(str[2] <= 0xbf)) {
return 3;
}
if ((str[0] == 0xed) && /* 5 */
(str[1] >= 0x80) &&
(str[1] <= 0x9f) &&
(str[2] >= 0x80) &&
(str[2] <= 0xbf)) {
return 3;
}
if ((str[0] == 0xf0) && /* 6 */
(str[1] >= 0x90) &&
(str[1] <= 0xbf) &&
(str[2] >= 0x80) &&
(str[2] <= 0xbf) &&
(str[3] >= 0x80) &&
(str[3] <= 0xbf)) {
return 4;
}
if ((str[0] >= 0xf1) && /* 6 */
(str[1] <= 0xf3) &&
(str[1] >= 0x80) &&
(str[1] <= 0xbf) &&
(str[2] >= 0x80) &&
(str[2] <= 0xbf) &&
(str[3] >= 0x80) &&
(str[3] <= 0xbf)) {
return 4;
}
if ((str[0] == 0xf4) && /* 7 */
(str[1] >= 0x80) &&
(str[1] <= 0x8f) &&
(str[2] >= 0x80) &&
(str[2] <= 0xbf) &&
(str[3] >= 0x80) &&
(str[3] <= 0xbf)) {
return 4;
}
/*
* Invalid UTF-8 Character
*/
return 0;
}
size_t escape_string(char const *in, size_t inlen, char *out, size_t outlen, bool do_utf8)
{
uint8_t const *p = (uint8_t const *) in;
int sp = 0;
int utf8 = 0;
size_t freespace = outlen;
/* Can't '\0' terminate */
if (freespace == 0) {
return inlen;
}
/* No input, so no output... */
if (!in) {
no_input:
*out = '\0';
return 0;
}
/* Figure out the length of the input string */
if (inlen == 0) inlen = strlen(in);
/* Not enough space to hold one char */
if (freespace < 2) {
/* And there's input data... */
if (inlen > 0) {
*out = '\0';
return inlen;
}
goto no_input;
}
while (inlen > 0) {
/*
* Hack: never print trailing zero.
* Some clients send pings with an off-by-one
* length (confused with strings in C).
*/
if ((inlen == 1) && (*p == '\0')) {
inlen--;
break;
}
switch (*p) {
case '\\':
sp = '\\';
break;
case '\r':
sp = 'r';
break;
case '\n':
sp = 'n';
break;
case '\t':
sp = 't';
break;
case '"':
sp = '"';
break;
default:
sp = '\0';
break;
}
if (sp) {
if (freespace < 3) break; /* \ + <c> + \0 */
*out++ = '\\';
*out++ = sp;
freespace -= 2;
p++;
inlen--;
continue;
}
if (do_utf8) {
utf8 = utf8_char(p);
} else {
if (utf8 == 0) {
if (freespace < 5) break; /* \ + <o><o><o> + \0 */
snprintf(out, freespace, "\\%03o", *p);
out += 4;
freespace -= 4;
p++;
inlen--;
continue;
}
do {
if (freespace < 2) goto finish; /* <c> + \0 */
*out++ = *p++;
freespace--;
inlen--;
} while (--utf8 > 0);
}
finish:
*out = '\0';
/* Indicate truncation occurred */
if (inlen > 0) return outlen + inlen;
return outlen - freespace;
}
static char const *hextab = "0123456789abcdef";
size_t hex2bin(uint8_t *bin, char const *hex, size_t outlen)
{
size_t i;
char *c1, *c2;
for (i = 0; i < outlen; i++) {
if(!(c1 = memchr(hextab, tolower((int) hex[i << 1]), 16)) ||
!(c2 = memchr(hextab, tolower((int) hex[(i << 1) + 1]), 16)))
break;
bin[i] = ((c1-hextab)<<4) + (c2-hextab);
}
return i;
}
int main(int argc, char **argv)
{
char *p;
char strbuff[1024];
char sanbuff[sizeof(strbuff)];
uint8_t binbuff[sizeof(sanbuff) / 2];
char octbuff[(sizeof(sanbuff) * 4) + 1]; /* trailing \0 */
size_t s, i;
p = sanbuff;
octbuff[0] = '\0';
while ((s = read(STDIN_FILENO, strbuff, sizeof(strbuff))) > 0) {
evenise:
for (i = 0; i < s; i++) {
if ((strbuff[i] == '0') && ((strbuff[i + 1] == 'x') || (strbuff[i + 1] == 'X'))) continue;
switch (strbuff[i]) {
case 'A':
*p++ = 'a';
break;
case 'B':
*p++ = 'b';
break;
case 'C':
*p++ = 'c';
break;
case 'D':
*p++ = 'd';
break;
case 'E':
*p++ = 'e';
break;
case 'F':
*p++ = 'f';
break;
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
*p++ = strbuff[i];
break;
default:
continue;
}
}
/* Ug, uneven length string */
if (((p - sanbuff) % 2)) {
s = read(STDIN_FILENO, strbuff, 1);
if (s > 0) goto evenise;
}
s = hex2bin(binbuff, sanbuff, p - sanbuff);
escape_string((char *) binbuff, s, octbuff, sizeof(octbuff));
puts(sanbuff);
puts(octbuff);
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment