C interop in V
Published .. 05-07-2024
Type ....... article
Tags ....... v, c
Type ....... article
Tags ....... v, c
V has great C interop. Here are an example taken from a program which checks different issues with a website. This example shows how to the the expiring date of the sites certificate using C interop.
V uses the mbedtls library, but not all of this library is exposed from V. For instance there are no functions to access the details of the certificate, only function to check for validity is supplied.
But we can access the full mbedtls library by declaring the structures and function you whish to use from V.
module main
import time
import cli
import os
import net.mbedtls
// Declare that the following is a typedef declared in C. Used in function declararions. After declaration it can be used from V. But as you can see, no properties are made available.
@[typedef]
pub struct C.mbedtls_x509_crt {
}
// We can have our own V version of the C structures. The version can have properties.
pub struct Mbedtls_x509_buf { // x509.h : 228 (is a mbedtls_asn1_buf)
tag int
len isize // in C the type is size_t and the corrosponding type in v is isize (i used i64 until i found out)
p &char
}
// Our local struct can have methods
fn (n Mbedtls_x509_buf) str() string {
unsafe {
vs := n.p.vstring_literal_with_len(int(n.len))
return '${vs}'
}
}
@[typedef]
pub struct C.Mbedtls_x509_name {
}
pub struct Mbedtls_x509_name { // x509.h : 239 (is a mbedtls_asn1_named_data)
oid Mbedtls_x509_buf
val Mbedtls_x509_buf
next &Mbedtls_x509_name
merged u8
}
fn (n Mbedtls_x509_name) str() string {
mut r := n.val.str()
if n.next != C.NULL {
r += ' [ ' + n.next.str() + ' ] '
}
return r
}
@[typedef]
pub struct C.mbedtls_x509_time {
}
pub struct Mbedtls_x509_time { // x509.h : 247
year int
mon int
day int
hour int
min int
sec int
}
fn (t Mbedtls_x509_time) time() time.Time {
return time.Time{
year: t.year
month: t.mon
day: t.day
hour: t.hour
minute: t.min
second: t.sec
}
}
fn (t Mbedtls_x509_time) str() string {
return t.time().format_rfc3339()
}
// The struct that hold the certificate, the mbedtls has further fields, I have only declared the first few. So this struct does not have same size as the C equivalent.
pub struct Mbedtls_x509_crt_view { // x509_crt.h : 54
own int
raw Mbedtls_x509_buf
tbs Mbedtls_x509_buf
version int
serial Mbedtls_x509_buf
sig_oid Mbedtls_x509_buf
issuer_raw Mbedtls_x509_buf
subject_raw Mbedtls_x509_buf
issuer Mbedtls_x509_name
subject Mbedtls_x509_name
valid_from Mbedtls_x509_time
valid_to Mbedtls_x509_time
}
// This function is not exposed in the stdlibs implementation but i can easily declare it and use it from V.
fn C.mbedtls_ssl_get_peer_cert(sslctx &C.mbedtls_ssl_context) &C.mbedtls_x509_crt
// Retrieve and print info about the certificate of a given URL.
fn cert_expire_mbedtls(url string) {
host := url.all_after_first('//')
println('CERTEXPIRE')
println('URL : ${url}')
println('HOST : ${host}')
mut client := mbedtls.new_ssl_conn(mbedtls.SSLConnectConfig{
validate: false
}) or {
eprintln('new_ssl_conn failed: ${err}')
return
}
client.dial(host, 443) or {
eprintln('dialing failed: ${err}')
return
}
// Use declared C function to get the peer certificate
cert := C.mbedtls_ssl_get_peer_cert(voidptr(&client.ssl))
if cert == C.NULL {
eprintln('no certificate found')
return
}
// Use out V version of C.bbedtls_x509_crt_view to be able to access the properties of the struct
xc := &Mbedtls_x509_crt_view(cert)
println('CERT INFO:')
println(' Version : ${xc.version}')
println(' Issuer : ${xc.issuer}')
println(' Valid from : ${xc.valid_from}')
rel := xc.valid_to.time().relative_short()
println(' Valid to : ${xc.valid_to} [${rel}]')
}
fn main() {
mut app := cli.Command{
name: 'certexp'
description: 'check certificate expiring date'
commands: [
cli.Command{
name: 'cert'
required_args: 1
execute: fn (cmd cli.Command) ! {
for c in cmd.args {
// cert_expire(c)
cert_expire_mbedtls(c)
}
return
}
},
]
}
app.setup()
app.parse(os.args)
}