blob: 7bd65ef65ff9a27b560fc3a1d1deda229c93e9bb [file] [log] [blame] [raw]
/*
* Copyright (C) Xiaozhe Wang (chaoslawful)
* Copyright (C) Yichun Zhang (agentzh)
*/
#ifndef DDEBUG
#define DDEBUG 0
#endif
#include "ddebug.h"
#include "ngx_http_lua_string.h"
#include "ngx_http_lua_util.h"
#include "ngx_http_lua_args.h"
#include "ngx_crc32.h"
#if NGX_HAVE_SHA1
#include "ngx_sha1.h"
#endif
#include "ngx_md5.h"
#if (NGX_OPENSSL)
#include <openssl/evp.h>
#include <openssl/hmac.h>
#endif
#ifndef SHA_DIGEST_LENGTH
#define SHA_DIGEST_LENGTH 20
#endif
static uintptr_t ngx_http_lua_ngx_escape_sql_str(u_char *dst, u_char *src,
size_t size);
static int ngx_http_lua_ngx_escape_uri(lua_State *L);
static int ngx_http_lua_ngx_unescape_uri(lua_State *L);
static int ngx_http_lua_ngx_quote_sql_str(lua_State *L);
static int ngx_http_lua_ngx_md5(lua_State *L);
static int ngx_http_lua_ngx_md5_bin(lua_State *L);
#if (NGX_HAVE_SHA1)
static int ngx_http_lua_ngx_sha1_bin(lua_State *L);
#endif
static int ngx_http_lua_ngx_decode_base64(lua_State *L);
static int ngx_http_lua_ngx_encode_base64(lua_State *L);
static int ngx_http_lua_ngx_crc32_short(lua_State *L);
static int ngx_http_lua_ngx_crc32_long(lua_State *L);
static int ngx_http_lua_ngx_encode_args(lua_State *L);
static int ngx_http_lua_ngx_decode_args(lua_State *L);
#if (NGX_OPENSSL)
static int ngx_http_lua_ngx_hmac_sha1(lua_State *L);
#endif
void
ngx_http_lua_inject_string_api(lua_State *L)
{
lua_pushcfunction(L, ngx_http_lua_ngx_escape_uri);
lua_setfield(L, -2, "escape_uri");
lua_pushcfunction(L, ngx_http_lua_ngx_unescape_uri);
lua_setfield(L, -2, "unescape_uri");
lua_pushcfunction(L, ngx_http_lua_ngx_encode_args);
lua_setfield(L, -2, "encode_args");
lua_pushcfunction(L, ngx_http_lua_ngx_decode_args);
lua_setfield(L, -2, "decode_args");
lua_pushcfunction(L, ngx_http_lua_ngx_quote_sql_str);
lua_setfield(L, -2, "quote_sql_str");
lua_pushcfunction(L, ngx_http_lua_ngx_decode_base64);
lua_setfield(L, -2, "decode_base64");
lua_pushcfunction(L, ngx_http_lua_ngx_encode_base64);
lua_setfield(L, -2, "encode_base64");
lua_pushcfunction(L, ngx_http_lua_ngx_md5_bin);
lua_setfield(L, -2, "md5_bin");
lua_pushcfunction(L, ngx_http_lua_ngx_md5);
lua_setfield(L, -2, "md5");
#if (NGX_HAVE_SHA1)
lua_pushcfunction(L, ngx_http_lua_ngx_sha1_bin);
lua_setfield(L, -2, "sha1_bin");
#endif
lua_pushcfunction(L, ngx_http_lua_ngx_crc32_short);
lua_setfield(L, -2, "crc32_short");
lua_pushcfunction(L, ngx_http_lua_ngx_crc32_long);
lua_setfield(L, -2, "crc32_long");
#if (NGX_OPENSSL)
lua_pushcfunction(L, ngx_http_lua_ngx_hmac_sha1);
lua_setfield(L, -2, "hmac_sha1");
#endif
}
static int
ngx_http_lua_ngx_escape_uri(lua_State *L)
{
size_t len, dlen;
uintptr_t escape;
u_char *src, *dst;
if (lua_gettop(L) != 1) {
return luaL_error(L, "expecting one argument");
}
if (lua_isnil(L, 1)) {
lua_pushliteral(L, "");
return 1;
}
src = (u_char *) luaL_checklstring(L, 1, &len);
if (len == 0) {
return 1;
}
escape = 2 * ngx_http_lua_escape_uri(NULL, src, len, NGX_ESCAPE_URI);
if (escape) {
dlen = escape + len;
dst = lua_newuserdata(L, dlen);
ngx_http_lua_escape_uri(dst, src, len, NGX_ESCAPE_URI);
lua_pushlstring(L, (char *) dst, dlen);
}
return 1;
}
static int
ngx_http_lua_ngx_unescape_uri(lua_State *L)
{
size_t len, dlen;
u_char *p;
u_char *src, *dst;
if (lua_gettop(L) != 1) {
return luaL_error(L, "expecting one argument");
}
if (lua_isnil(L, 1)) {
lua_pushliteral(L, "");
return 1;
}
src = (u_char *) luaL_checklstring(L, 1, &len);
/* the unescaped string can only be smaller */
dlen = len;
p = lua_newuserdata(L, dlen);
dst = p;
ngx_http_lua_unescape_uri(&dst, &src, len, NGX_UNESCAPE_URI_COMPONENT);
lua_pushlstring(L, (char *) p, dst - p);
return 1;
}
static int
ngx_http_lua_ngx_quote_sql_str(lua_State *L)
{
size_t len, dlen, escape;
u_char *p;
u_char *src, *dst;
if (lua_gettop(L) != 1) {
return luaL_error(L, "expecting one argument");
}
src = (u_char *) luaL_checklstring(L, 1, &len);
if (len == 0) {
dst = (u_char *) "''";
dlen = sizeof("''") - 1;
lua_pushlstring(L, (char *) dst, dlen);
return 1;
}
escape = ngx_http_lua_ngx_escape_sql_str(NULL, src, len);
dlen = sizeof("''") - 1 + len + escape;
p = lua_newuserdata(L, dlen);
dst = p;
*p++ = '\'';
if (escape == 0) {
p = ngx_copy(p, src, len);
} else {
p = (u_char *) ngx_http_lua_ngx_escape_sql_str(p, src, len);
}
*p++ = '\'';
if (p != dst + dlen) {
return NGX_ERROR;
}
lua_pushlstring(L, (char *) dst, p - dst);
return 1;
}
static uintptr_t
ngx_http_lua_ngx_escape_sql_str(u_char *dst, u_char *src, size_t size)
{
ngx_uint_t n;
if (dst == NULL) {
/* find the number of chars to be escaped */
n = 0;
while (size) {
/* the highest bit of all the UTF-8 chars
* is always 1 */
if ((*src & 0x80) == 0) {
switch (*src) {
case '\0':
case '\b':
case '\n':
case '\r':
case '\t':
case 26: /* \z */
case '\\':
case '\'':
case '"':
n++;
break;
default:
break;
}
}
src++;
size--;
}
return (uintptr_t) n;
}
while (size) {
if ((*src & 0x80) == 0) {
switch (*src) {
case '\0':
*dst++ = '\\';
*dst++ = '0';
break;
case '\b':
*dst++ = '\\';
*dst++ = 'b';
break;
case '\n':
*dst++ = '\\';
*dst++ = 'n';
break;
case '\r':
*dst++ = '\\';
*dst++ = 'r';
break;
case '\t':
*dst++ = '\\';
*dst++ = 't';
break;
case 26:
*dst++ = '\\';
*dst++ = 'z';
break;
case '\\':
*dst++ = '\\';
*dst++ = '\\';
break;
case '\'':
*dst++ = '\\';
*dst++ = '\'';
break;
case '"':
*dst++ = '\\';
*dst++ = '"';
break;
default:
*dst++ = *src;
break;
}
} else {
*dst++ = *src;
}
src++;
size--;
} /* while (size) */
return (uintptr_t) dst;
}
static int
ngx_http_lua_ngx_md5(lua_State *L)
{
u_char *src;
size_t slen;
ngx_md5_t md5;
u_char md5_buf[MD5_DIGEST_LENGTH];
u_char hex_buf[2 * sizeof(md5_buf)];
if (lua_gettop(L) != 1) {
return luaL_error(L, "expecting one argument");
}
if (lua_isnil(L, 1)) {
src = (u_char *) "";
slen = 0;
} else {
src = (u_char *) luaL_checklstring(L, 1, &slen);
}
ngx_md5_init(&md5);
ngx_md5_update(&md5, src, slen);
ngx_md5_final(md5_buf, &md5);
ngx_hex_dump(hex_buf, md5_buf, sizeof(md5_buf));
lua_pushlstring(L, (char *) hex_buf, sizeof(hex_buf));
return 1;
}
static int
ngx_http_lua_ngx_md5_bin(lua_State *L)
{
u_char *src;
size_t slen;
ngx_md5_t md5;
u_char md5_buf[MD5_DIGEST_LENGTH];
if (lua_gettop(L) != 1) {
return luaL_error(L, "expecting one argument");
}
if (lua_isnil(L, 1)) {
src = (u_char *) "";
slen = 0;
} else {
src = (u_char *) luaL_checklstring(L, 1, &slen);
}
dd("slen: %d", (int) slen);
ngx_md5_init(&md5);
ngx_md5_update(&md5, src, slen);
ngx_md5_final(md5_buf, &md5);
lua_pushlstring(L, (char *) md5_buf, sizeof(md5_buf));
return 1;
}
#if (NGX_HAVE_SHA1)
static int
ngx_http_lua_ngx_sha1_bin(lua_State *L)
{
u_char *src;
size_t slen;
ngx_sha1_t sha;
u_char sha_buf[SHA_DIGEST_LENGTH];
if (lua_gettop(L) != 1) {
return luaL_error(L, "expecting one argument");
}
if (lua_isnil(L, 1)) {
src = (u_char *) "";
slen = 0;
} else {
src = (u_char *) luaL_checklstring(L, 1, &slen);
}
dd("slen: %d", (int) slen);
ngx_sha1_init(&sha);
ngx_sha1_update(&sha, src, slen);
ngx_sha1_final(sha_buf, &sha);
lua_pushlstring(L, (char *) sha_buf, sizeof(sha_buf));
return 1;
}
#endif
static int
ngx_http_lua_ngx_decode_base64(lua_State *L)
{
ngx_str_t p, src;
if (lua_gettop(L) != 1) {
return luaL_error(L, "expecting one argument");
}
if (lua_type(L, 1) != LUA_TSTRING) {
return luaL_error(L, "string argument only");
}
src.data = (u_char *) luaL_checklstring(L, 1, &src.len);
p.len = ngx_base64_decoded_length(src.len);
p.data = lua_newuserdata(L, p.len);
if (ngx_decode_base64(&p, &src) == NGX_OK) {
lua_pushlstring(L, (char *) p.data, p.len);
} else {
lua_pushnil(L);
}
return 1;
}
static int
ngx_http_lua_ngx_encode_base64(lua_State *L)
{
ngx_str_t p, src;
if (lua_gettop(L) != 1) {
return luaL_error(L, "expecting one argument");
}
if (lua_isnil(L, 1)) {
src.data = (u_char *) "";
src.len = 0;
} else {
src.data = (u_char *) luaL_checklstring(L, 1, &src.len);
}
p.len = ngx_base64_encoded_length(src.len);
p.data = lua_newuserdata(L, p.len);
ngx_encode_base64(&p, &src);
lua_pushlstring(L, (char *) p.data, p.len);
return 1;
}
static int
ngx_http_lua_ngx_crc32_short(lua_State *L)
{
u_char *p;
size_t len;
if (lua_gettop(L) != 1) {
return luaL_error(L, "expecting one argument, but got %d",
lua_gettop(L));
}
p = (u_char *) luaL_checklstring(L, 1, &len);
lua_pushnumber(L, (lua_Number) ngx_crc32_short(p, len));
return 1;
}
static int
ngx_http_lua_ngx_crc32_long(lua_State *L)
{
u_char *p;
size_t len;
if (lua_gettop(L) != 1) {
return luaL_error(L, "expecting one argument, but got %d",
lua_gettop(L));
}
p = (u_char *) luaL_checklstring(L, 1, &len);
lua_pushnumber(L, (lua_Number) ngx_crc32_long(p, len));
return 1;
}
static int
ngx_http_lua_ngx_encode_args(lua_State *L)
{
ngx_str_t args;
if (lua_gettop(L) != 1) {
return luaL_error(L, "expecting 1 argument but seen %d",
lua_gettop(L));
}
luaL_checktype(L, 1, LUA_TTABLE);
ngx_http_lua_process_args_option(NULL, L, 1, &args);
lua_pushlstring(L, (char *) args.data, args.len);
return 1;
}
static int
ngx_http_lua_ngx_decode_args(lua_State *L)
{
u_char *buf;
u_char *tmp;
size_t len = 0;
int n;
int max;
n = lua_gettop(L);
if (n != 1 && n != 2) {
return luaL_error(L, "expecting 1 or 2 arguments but seen %d", n);
}
buf = (u_char *) luaL_checklstring(L, 1, &len);
if (n == 2) {
max = luaL_checkint(L, 2);
lua_pop(L, 1);
} else {
max = NGX_HTTP_LUA_MAX_ARGS;
}
tmp = lua_newuserdata(L, len);
ngx_memcpy(tmp, buf, len);
lua_createtable(L, 0, 4);
return ngx_http_lua_parse_args(L, tmp, tmp + len, max);
}
#if (NGX_OPENSSL)
static int
ngx_http_lua_ngx_hmac_sha1(lua_State *L)
{
u_char *sec, *sts;
size_t lsec, lsts;
unsigned int md_len;
unsigned char md[EVP_MAX_MD_SIZE];
const EVP_MD *evp_md;
if (lua_gettop(L) != 2) {
return luaL_error(L, "expecting one argument, but got %d",
lua_gettop(L));
}
sec = (u_char *) luaL_checklstring(L, 1, &lsec);
sts = (u_char *) luaL_checklstring(L, 2, &lsts);
evp_md = EVP_sha1();
HMAC(evp_md, sec, lsec, sts, lsts, md, &md_len);
lua_pushlstring(L, (char *) md, md_len);
return 1;
}
#endif
#ifndef NGX_HTTP_LUA_NO_FFI_API
void
ngx_http_lua_ffi_md5_bin(const u_char *src, size_t len, u_char *dst)
{
ngx_md5_t md5;
ngx_md5_init(&md5);
ngx_md5_update(&md5, src, len);
ngx_md5_final(dst, &md5);
}
void
ngx_http_lua_ffi_md5(const u_char *src, size_t len, u_char *dst)
{
ngx_md5_t md5;
u_char md5_buf[MD5_DIGEST_LENGTH];
ngx_md5_init(&md5);
ngx_md5_update(&md5, src, len);
ngx_md5_final(md5_buf, &md5);
ngx_hex_dump(dst, md5_buf, sizeof(md5_buf));
}
int
ngx_http_lua_ffi_sha1_bin(const u_char *src, size_t len, u_char *dst)
{
#if NGX_HAVE_SHA1
ngx_sha1_t sha;
ngx_sha1_init(&sha);
ngx_sha1_update(&sha, src, len);
ngx_sha1_final(dst, &sha);
return 1;
#else
return 0;
#endif
}
size_t
ngx_http_lua_ffi_encode_base64(const u_char *src, size_t slen, u_char *dst)
{
ngx_str_t in, out;
in.data = (u_char *) src;
in.len = slen;
out.data = dst;
ngx_encode_base64(&out, &in);
return out.len;
}
int
ngx_http_lua_ffi_decode_base64(const u_char *src, size_t slen, u_char *dst,
size_t *dlen)
{
ngx_int_t rc;
ngx_str_t in, out;
in.data = (u_char *) src;
in.len = slen;
out.data = dst;
rc = ngx_decode_base64(&out, &in);
*dlen = out.len;
return rc == NGX_OK;
}
size_t
ngx_http_lua_ffi_unescape_uri(const u_char *src, size_t len, u_char *dst)
{
u_char *p = dst;
ngx_http_lua_unescape_uri(&p, (u_char **) &src, len,
NGX_UNESCAPE_URI_COMPONENT);
return p - dst;
}
size_t
ngx_http_lua_ffi_uri_escaped_length(const u_char *src, size_t len)
{
return len + 2 * ngx_http_lua_escape_uri(NULL, (u_char *) src, len,
NGX_ESCAPE_URI);
}
void
ngx_http_lua_ffi_escape_uri(const u_char *src, size_t len, u_char *dst)
{
ngx_http_lua_escape_uri(dst, (u_char *) src, len, NGX_ESCAPE_URI);
}
#endif
/* vi:set ft=c ts=4 sw=4 et fdm=marker: */