Regex Format in Conditional DNS forwarding

I have created a this REGEXP below:

^(?![\w]*[-][\d]{2})(.*[\.]?ad\.example\.com)$

But doesn’t work on Mikrotik Layer7 RegExp. Can anyone help me to convert it to mikrotik?


Why would you use this in Layer7 when dns already allows for regex and forwarding requests

Because I want to forward DNS requests to different servers based on hostname request.

Using this rules:

/ip firewall layer7-protocol
add name=testdns regexp=lantest.mindlesstux.com

/ip firewall nat
add action=dst-nat chain=dstnat disabled=no dst-address=4.2.2.2 dst-port=53 layer7-protocol=testdns protocol=udp to-addresses=8.8.8.8 to-ports=53
add action=dst-nat chain=dstnat disabled=no dst-address=4.2.2.2 dst-port=53 layer7-protocol=testdns protocol=tcp to-addresses=8.8.8.8 to-ports=53

Apparently you can do it for a host in regexp. But using the expression that I post above I can filter requests based on my needs.


Sorry, I just see that is possible to add regexp directly to DNS static. But even doing that, it’s not working:

/ip/dns/static> add regexp="^(?![\\w]*[-][\\d]{2})(.*[\\.]?ad\\.localdomain)$" forward-to=10.10.10.10
failure: name or regexp required

RegExp is not working correctly for me.

Maybe I misunderstand what you are trying to do, but everything you said can be done by dns.

/ip dns static add forward-to=8.8.8.8 match-subdomain=yes name=example.com type=FWD

Hello,

I did it, look:

> /ip dns static add regexp="^(?![\\w]*[-][\\d]{2})(.*[\\.]?ad\\.localdomain)$" match-subdomain=yes type=FWD forward-to=10.10.10.10             
failure: name or regexp required

My regexp doesn’t work. Don’t know why. I have checked it several times in https://regex101.com/.

Can you explain in plain language what you are trying to match?

That isn’t a “POSIX basic regular expression” (BRE) that this setting is documented as taking. It’s vaguely PCRE style, though with odd variations like with the double-escape of things like “\d”. It might be a valid POSIX extended regex (ERE), but I can’t be bothered to validate it for you given that RouterOS doesn’t take ERE, leaving me little incentive to bother.


I have checked it several times in > https://regex101.com/> .

The 8 “flavors” of RE offered by that site include neither POSIX BRE nor ERE. You’re using the wrong tool to convince yourself that this should work.

Test with something that speaks BRE natively instead, something like GNU grep, which assumes BRE when not given the -E flag. A good guide to the differences is Linux’s regex(7) man page.

If you’re using BSD grep — as on modern macOS — you might need to give -G for full BRE compatibility.

Always know which flavor your RE processor expects. The 10 considered here aren’t even all there is. I’d count Vim’s RE format as an eleventh distinct flavor, for example.

@tanget’s right, RouterOS is closer to the C-runtime’s more limited subset. But I’m not sure your doing anything tricky in the regex…

You can run a simple test on RouterOS CLI to check a regex too:

:put ("aaa"~"[a]{3}")      
# true
:put ("aaa"~"[a]{4}") 
# false

But often what is more problematic is RouterOS’s string interpolation rules - since regex’s can have variables (not in /ip/dns/static, but generally, regardless string rules still apply). So the final $" is at least one problem, since that need to be escaped for RouterOS since $ is reserved for variable substitution, like $.

RouterOS will process the string BEFORE regex evaluation, so stuff like " and \ also need to be escaped, " or \. The “?” is a special character in RouterOS v6, so it need to be escaped - but NOT in V7.

:put doesn’t work for me. How do you debug scripts placed inside system / scripts? Every :put inside the script just is not being displayed.

I am using v7.

Could you help me to rewrite this regexp ^(?![\w][-][\d]{2})(.[\.]?ad\.localdomain)$ to work correctly in RouteOS V7?


If using :put with your regex does not work to match true/false correct, it ain’t going to work as a /system/script. As I said the $ is WRONG and needs escaping. A :put at the CLI would catch that.

You’ve never said what it should match, so hard to know what’s right/wrong (and the ?! may not be allowed). Assuming the $ becomes a $ - you can use the :put (“matchtest”~“[m][a][t][c][h].*”) like you’re using regex101 to test it.

:put ("test-01.ad.localdomain"~"^(?![\\w]*[-][\\d]{2})(.*[\\.]?ad\\.localdomain)\$")
# false
 :put ("matchtest"~"[m][a][t][c][h].*")
# true
:put ("matchtest"~"^[t][e][s][t].*") 
# false

But yeah in a /system/script, you need to use /log/info “msg”, instead of :put to “see” output from /system/script or schedule.

Simply RouterOS for RegEx use only POSIX standard without metadata (Character classes) like [:digit:] (stay for [0-9] ) or \d (again is equal to [0-9] ) from other languages
https://en.wikibooks.org/wiki/Regular_Expressions/POSIX_Basic_Regular_Expressions

So… no \w (but can be replaced with [a-zA-Z0-9_]), no \d (but can be replaced with [0-9]), no ?! (completely unsupported the negative lookahead), etc., etc., etc.

I am pretty sure I have code that uses “\w”…

Edit… Scratch that… It was “\b”. Confusing these two all the time. :squinting_face_with_tongue:

Still… Looks like “\w” does work:

> :put ("abc" ~ "^\\w\\w\\w\$")    
true

Yes, the obsolete help from MikroTik must be updated…

\w and \W work on both v6 and v7 (from what version? on 6.48.7 work),
\d and \D do NOT work on v6 (tested on 6.48.7) but work on v7 (tested on 7.15.1),

(?!) is not supported from both:

v7] > :put ("test0"~"test(?!\\d)")       
false

v7] > :put ("testX"~"test(?!\\d)") 
false
# must be true, ?! is unsupported

# but can be writed on this way, for same result...
v7] > :put ("testX"~"test\\D") 
true

Probably the wrong RegEx provided from the OP can be replaced with working one, if the OP provide some examples for what must match and what not…


Notice: Some section, like DNS, layer7, or script, apparently have different rules for RegEx syntax…

Thank you for helping me.

Sorry for that. I want to match anything [.]?ad.localdomain (with or without dot), including subdomains, except for subdomain where you have word-XX (where XX is a numeric value).

Using https://regex101.com/ you can easy debug the regex. With your tips now I will try to debug correctly the tests.

I have tried different ways:

 > :put ("test.ad.localdomain"~"^(?![a-zA-z0-9]*[-][0-9]{2})(.*[\\.]?ad\\.localdomain)\$")   
false
 > :put ("test.ad.localdomain"~"^([\\W]*[-][\\D]{2})(.*[\\.]?ad\\.localdomain)\$")   
false
 > :put ("test.ad.localdomain"~"^([\\W]*[-][\\D]{2})(\\.*[\\.]?ad\\.localdomain)\$")
false

Using this :put will help me to try to debug it… I will keep trying.

Ok!

Now I am matching, but the first group I want to negative it. ?! just doesn’t work.

> :put ("test-01.ad.localdomain"~"^([a-zA-Z0-9]*[-][0-9]{2})(\\.*[\\.]?ad\\.localdomain)\$")
true
> :put ("test.ad.localdomain"~"^(?![a-zA-Z0-9]*[-][0-9]{2})(\\.*[\\.]?ad\\.localdomain)\$")
false

I’m almost giving up… LOL!

Thank you for everybody that participate to this thread.

I finally did it:

For who want to do something similar, follow the working regex:

:put ("test-01.ad.localdomain"~"([^a-zA-Z0-9]*[^-][^0-9]{2})(\\.*[\\.]?ad\\.localdomain)\$")
:put ("test.ad.localdomain"~"([^a-zA-Z0-9]*[^-][^0-9]{2})(\\.*[\\.]?ad\\.localdomain)\$")

My DHCP update my static DNS to the standard [machine|print]-XX where XX is a numeric value, so I have to resolve DNS using RB. For all others records ad.localdomain, I want to check the AD DNS.

It’s working almost perfectly now.

I don’t know why, but the ad.localdomain should match the rule too, but it’s not.